///////////////////////////////////////////////////////////////////////////////
//
// DBT86 Project : x86 Platform : x86 Interpreter : ALU instructions
// Copyright (C) 2004 by Ben Hertzberg and Stanford University
// All Rights Reserved
//
// This code may not be used for any purpose without the express written
// consent of the author.  See the LICENSE.txt file for details.
//
//
// DESCRIPTION
//
// This unit implements simulation of integer data manipulation on x86.
//
// TODO:
// - see TODO.txt for additional opcodes needing implementation
//
///////////////////////////////////////////////////////////////////////////////

#include "interp.h"
#ifndef LESS86
#include "CPU.h"
#endif

// These tables are used in rotate-through-carry instructions
static BYTE Mod9Table[32] = {
  0, 1, 2, 3, 4, 5, 6, 7, 8,
  0, 1, 2, 3, 4, 5, 6, 7, 8,
  0, 1, 2, 3, 4, 5, 6, 7, 8,
  0, 1, 2, 3, 4
};
static BYTE Mod17Table[32] = {
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
};

// These tables are used in bit-scan instructions
static BYTE BitScanForwardTable[256] = {
  0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
};
static BYTE BitScanReverseTable[256] = {
  0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
};


// In these macros we rely on the compiler to be smart enough to optimize if(const) for performance,
// as well as copy propagation and dead code elimination.  All of these are really basic optimizations.
// Another helpful optimization is x & 0xFFFFFFFF => x on 32-bit systems.

#define LOCKED (X86_TMP(prefixes) & X86_DEC_PREFIX_LOCK)
//#define LOCKED 0

// with_carry and no_result are assumed to be exclusive, as is true of x86 opcodes
#define BINARY_EB_GB_OP(op,flags_const,with_carry,no_result, lock_impl) { \
    IVAL dst,src; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    if (no_result) {\
      LOAD_RM_R_BYTE(dst); \
    } else { \
      LOAD_RM_RW_BYTE(dst); \
    } \
    src = LOAD_REG_BYTE; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_RM_BYTE(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_RM_BYTE(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define BINARY_EW_GW_OP(op,flags_const,with_carry,no_result, lock_impl) { \
    IVAL dst,src; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    if (no_result) { \
      LOAD_RM_R_WORD(dst); \
    } else { \
      LOAD_RM_RW_WORD(dst); \
    } \
    src = LOAD_REG_WORD; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_RM_WORD(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_RM_WORD(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define BINARY_ED_GD_OP(op,flags_const,with_carry,no_result, lock_impl) { \
    IVAL dst,src; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    if (no_result) { \
      LOAD_RM_R_DWORD(dst); \
    } else { \
      LOAD_RM_RW_DWORD(dst); \
    } \
    src = LOAD_REG_DWORD; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_RM_DWORD(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_RM_DWORD(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define BINARY_GB_EB_OP(op,flags_const,with_carry,no_result) { \
    IVAL dst,src; \
    dst = LOAD_REG_BYTE; \
    LOAD_RM_R_BYTE(src); \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_REG_BYTE(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_REG_BYTE(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
  }

#define BINARY_GW_EW_OP(op,flags_const,with_carry,no_result) { \
    IVAL dst,src; \
    dst = LOAD_REG_WORD; \
    LOAD_RM_R_WORD(src); \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_REG_WORD(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_REG_WORD(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
  }

#define BINARY_GD_ED_OP(op,flags_const,with_carry,no_result) { \
    IVAL dst,src; \
    dst = LOAD_REG_DWORD; \
    LOAD_RM_R_DWORD(src); \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_REG_DWORD(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_REG_DWORD(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
  }

#define BINARY_EB_I_OP(op,flags_const,with_carry,no_result,lock_impl) { \
    IVAL dst,src; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    if (no_result) {\
      LOAD_RM_R_BYTE(dst); \
    } else { \
      LOAD_RM_RW_BYTE(dst); \
    } \
    src = LOAD_IMM_B; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_RM_BYTE(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_RM_BYTE(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define BINARY_EW_I_OP(op,flags_const,with_carry,no_result,lock_impl) { \
    IVAL dst,src; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    if (no_result) { \
      LOAD_RM_R_WORD(dst); \
    } else { \
      LOAD_RM_RW_WORD(dst); \
    } \
    src = LOAD_IMM_W; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_RM_WORD(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_RM_WORD(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define BINARY_ED_I_OP(op,flags_const,with_carry,no_result,lock_impl) { \
    IVAL dst,src; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    if (no_result) { \
      LOAD_RM_R_DWORD(dst); \
    } else { \
      LOAD_RM_RW_DWORD(dst); \
    } \
    src = LOAD_IMM_D; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_RM_DWORD(res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_RM_DWORD(res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define BINARY_AL_IB_OP(op,flags_const,with_carry,no_result) { \
    IVAL dst,src; \
    dst = LOAD_REG_EAX & 0xFF; \
    src = LOAD_IMM_B; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_REG_ID_BYTE(AL,res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_REG_ID_BYTE(AL,res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
  }

#define BINARY_AX_IW_OP(op,flags_const,with_carry,no_result) { \
    IVAL dst,src; \
    dst = LOAD_REG_EAX & 0xFFFF; \
    src = LOAD_IMM_W; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_REG_ID_WORD(AX,res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_REG_ID_WORD(AX,res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
  }

#define BINARY_EAX_ID_OP(op,flags_const,with_carry,no_result) { \
    IVAL dst,src; \
    dst = LOAD_REG_EAX & 0xFFFFFFFF; \
    src = LOAD_IMM_D; \
    if (with_carry) { \
      IVAL res, cflag; \
      cflag = GET_CARRY_FLAG_AS_BOOL; \
      res = dst op src op cflag; \
      STORE_REG_ID_DWORD(EAX,res); \
      SET_FLAGS_X(flags_const,dst,src,cflag); \
    } else { \
      if (!no_result) { \
        IVAL res; \
        res = dst op src; \
        STORE_REG_ID_DWORD(EAX,res); \
      } \
      SET_FLAGS(flags_const,dst,src);\
    } \
  }

#define REFLEXIVE_REG_WORD_OP(op,reg,arg, flags_const) { \
    IVAL src, res, tmp, cflag; \
    LOAD_REG_ID_WORD(src,reg); \
    cflag = GET_CARRY_FLAG_AS_BOOL; \
    tmp = arg; \
    res = src op tmp; \
    STORE_REG_ID_WORD(reg,res); \
    SET_FLAGS_X(flags_const,src,tmp,cflag);\
  }

#define REFLEXIVE_REG_DWORD_OP(op,reg,arg, flags_const) { \
    IVAL src, res, tmp, cflag; \
    LOAD_REG_ID_DWORD(src,reg); \
    cflag = GET_CARRY_FLAG_AS_BOOL; \
    tmp = arg; \
    res = src op tmp; \
    STORE_REG_ID_DWORD(reg,res); \
    SET_FLAGS_X(flags_const,src,tmp,cflag);\
  }

#define REFLEXIVE_EB_OP(op, arg, flags_const, lock_impl) { \
    IVAL src, res, tmp, cflag; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    LOAD_RM_RW_BYTE(src); \
    cflag = GET_CARRY_FLAG_AS_BOOL; \
    tmp = arg; \
    res = src op tmp; \
    STORE_RM_BYTE(res); \
    SET_FLAGS_X(flags_const,src,tmp,cflag);\
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define REFLEXIVE_EW_OP(op, arg, flags_const, lock_impl) { \
    IVAL src, res, tmp, cflag; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    LOAD_RM_RW_WORD(src); \
    cflag = GET_CARRY_FLAG_AS_BOOL; \
    tmp = arg; \
    res = src op tmp; \
    STORE_RM_WORD(res); \
    SET_FLAGS_X(flags_const,src,tmp,cflag);\
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define REFLEXIVE_ED_OP(op, arg, flags_const, lock_impl) { \
    IVAL src, res, tmp, cflag; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    LOAD_RM_RW_DWORD(src); \
    cflag = GET_CARRY_FLAG_AS_BOOL; \
    tmp = arg; \
    res = src op tmp; \
    STORE_RM_DWORD(res); \
    SET_FLAGS_X(flags_const,src,tmp,cflag);\
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define UNARY_EB_OP(op, flags_const, update_flags, lock_impl) { \
    IVAL src, res; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    LOAD_RM_RW_BYTE(src); \
    res = op (SVAL)src; \
    STORE_RM_BYTE(res); \
    if (update_flags) { \
      SET_FLAGS_U(flags_const,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define UNARY_EW_OP(op, flags_const, update_flags, lock_impl) { \
    IVAL src, res; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    LOAD_RM_RW_WORD(src); \
    res = op (SVAL)src; \
    STORE_RM_WORD(res); \
    if (update_flags) { \
      SET_FLAGS_U(flags_const,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

#define UNARY_ED_OP(op, flags_const, update_flags, lock_impl) { \
    IVAL src, res; \
    if (LOCKED) { \
      curPE->lockMemory(); \
    } \
    LOAD_RM_RW_DWORD(src); \
    res = op (SVAL)src; \
    STORE_RM_DWORD(res); \
    if (update_flags) { \
      SET_FLAGS_U(flags_const,src);\
    } \
    if (LOCKED) { \
      curPE->unlockMemory(); \
    } \
  }

// note that CMOV faults even if condition is false
#define CMOV_GW_EW_OP(cond_evaluator) { \
    IVAL src; \
    LOAD_RM_R_WORD(src); \
    if (cond_evaluator) { \
      STORE_REG_WORD(src); \
    } \
  }

#define CMOV_GD_ED_OP(cond_evaluator) { \
    IVAL src; \
    LOAD_RM_R_DWORD(src); \
    if (cond_evaluator) { \
      STORE_REG_DWORD(src); \
    } \
  }

#define SETCC_EB_OP(cond_evaluator) { \
    IVAL cond; \
    cond = cond_evaluator; \
    STORE_RM_W_BYTE(cond); \
  }

#define MOV_REG_IB_OP(reg) { \
    IVAL src; \
    src = LOAD_IMM; \
    STORE_REG_ID_BYTE(reg, src); \
  }

#define MOV_REG_IW_OP(reg) { \
    IVAL src; \
    src = LOAD_IMM; \
    STORE_REG_ID_WORD(reg, src); \
  }

#define MOV_REG_ID_OP(reg) { \
    IVAL src; \
    src = LOAD_IMM; \
    STORE_REG_ID_DWORD(reg, src); \
  }

#define BSWAP_REG_OP(reg) { \
    IVAL src,res; \
    LOAD_REG_ID_DWORD(src,reg); \
    res = (src << 24) | ((src & 0x0000FF00) << 8) | ((src & 0x00FF0000) >> 8) | ((src >> 24) & 0xFF); \
    STORE_REG_ID_DWORD(reg,res); \
  }

#define XCHG_REG_WORD_OP(reg) { \
    IVAL dst,src; \
    dst = LOAD_REG_EAX & 0xFFFF; \
    LOAD_REG_ID_WORD(src,reg); \
    STORE_REG_ID_WORD(AX,src); \
    STORE_REG_ID_WORD(reg,dst); \
  }

#define XCHG_REG_DWORD_OP(reg) { \
    IVAL dst,src; \
    dst = LOAD_REG_EAX & 0xFFFFFFFF; \
    LOAD_REG_ID_DWORD(src,reg); \
    STORE_REG_ID_DWORD(EAX,src); \
    STORE_REG_ID_DWORD(reg,dst); \
  }

// shift by 0 does not set any flags, but could still fault on access
// rotates don't modify enough flags, which makes a mess - but rotates are rare anyway
// byte rotate masks to 3 bits, while byte shift masks to 5 bits... go figure, this seems contrary to Intel's own docs
// not only that, but a byte rotate with a count of 8 sets flags (masks to 5 bits, not 3)
// appears that rotate doesn't really mask to 3 bits, but its effect does

#define SHIFT_L_BYTE_OP(sa_source,is_rotate,through_carry,is_arithmetic,is_double) { \
    IVAL val, sa, ra, res; \
    IVAL cf; \
    sa = sa_source & 0x1F; \
    if (is_rotate) { \
      if (through_carry) { \
        ra = Mod9Table[sa]; \
      } else { \
        ra = sa & 0x7; \
      } \
    } \
    LOAD_RM_RW_BYTE(val); \
    if (sa) { \
      if (!is_rotate) { \
        res = val << sa; \
        SET_FLAGS(CF_SHL_BYTE,val,sa); \
      } else { \
        if (ra) { \
          if (!through_carry) { \
            res = (val << ra) | (val >> (8-ra)); \
            cf = res & 0x1; \
          } else { \
            res = (val << ra) | (GET_CARRY_FLAG_AS_BOOL << (ra-1)) | (val >> (9-ra)); \
            cf = ((val << (ra-1)) & 0x80) != 0; \
          } \
        } else { \
          res = val; \
          if (through_carry) { \
            cf = GET_CARRY_FLAG_AS_BOOL; \
          } else { \
            cf = res & 0x1; \
          } \
        } \
        SET_FLAGS_CF_OF(cf, (cf ^ ((res & 0x80) != 0))); \
      } \
    } else { \
      res = val; \
    } \
    STORE_RM_BYTE(res); \
  }

#define SHIFT_L_WORD_OP(sa_source,is_rotate,through_carry,is_arithmetic,is_double) { \
    IVAL val, sa, ra, res, tmp; \
    IVAL cf; \
    sa = sa_source & 0x1F; \
    if (is_rotate) { \
      if (through_carry) { \
        ra = Mod17Table[sa]; \
      } else { \
        ra = sa & 0xF; \
      } \
    } \
    LOAD_RM_RW_WORD(val); \
    if (sa) { \
      if (!is_rotate) { \
        if (!is_double) { \
          res = val << sa; \
        } else { \
          tmp = LOAD_REG_WORD; \
          res = (val << sa) | ((tmp >> (16-sa)) & ((1 << sa) - 1)); \
        } \
        SET_FLAGS(CF_SHL_WORD,val,sa); \
      } else { \
        if (ra) { \
          if (!through_carry) { \
            res = (val << ra) | (val >> (16-ra)); \
            cf = res & 0x1; \
          } else { \
            res = (val << ra) | (GET_CARRY_FLAG_AS_BOOL << (ra-1)) | (val >> (17-ra)); \
            cf = ((val << (ra-1)) & 0x8000) != 0; \
          } \
        } else { \
          res = val; \
          if (through_carry) { \
            cf = GET_CARRY_FLAG_AS_BOOL; \
          } else { \
            cf = res & 0x1; \
          } \
        } \
        SET_FLAGS_CF_OF(cf, (cf ^ ((res & 0x8000) != 0))); \
      } \
    } else { \
      res = val; \
    } \
    STORE_RM_WORD(res); \
  }

#define SHIFT_L_DWORD_OP(sa_source,is_rotate,through_carry,is_arithmetic,is_double) { \
    IVAL val, sa, ra, res, tmp; \
    IVAL cf; \
    sa = sa_source & 0x1F; \
    ra = sa; \
    LOAD_RM_RW_DWORD(val); \
    if (sa) { \
      if (!is_rotate) { \
        if (!is_double) { \
          res = val << sa; \
        } else { \
          tmp = LOAD_REG_DWORD; \
          res = (val << sa) | ((tmp >> (32-sa)) & ((1 << sa) - 1)); \
        } \
        SET_FLAGS(CF_SHL_DWORD,val,sa); \
      } else { \
        if (!through_carry) { \
          res = (val << ra) | (val >> (32-ra)); \
          cf = res & 0x1; \
        } else { \
          res = (val << ra) | (GET_CARRY_FLAG_AS_BOOL << (ra-1)) | ((ra == 1) ? 0 : (val >> (33-ra))); \
          cf = ((val << (ra-1)) & 0x80000000) != 0; \
        } \
        SET_FLAGS_CF_OF(cf, (cf ^ ((res & 0x80000000) != 0))); \
      } \
    } else { \
      res = val; \
    } \
    STORE_RM_DWORD(res); \
  }

#define SHIFT_R_BYTE_OP(sa_source,is_rotate,through_carry,is_arithmetic, is_double) { \
    IVAL val, sa, ra, res; \
    IVAL cf; \
    sa = sa_source & 0x1F; \
    if (is_rotate) { \
      if (through_carry) { \
        ra = Mod9Table[sa]; \
      } else { \
        ra = sa & 0x7; \
      } \
    } \
    LOAD_RM_RW_BYTE(val); \
    if (sa) { \
      if (!is_rotate) { \
        if (is_arithmetic) { \
          res = ((INT8)val) >> sa; \
          SET_FLAGS(CF_SAR_BYTE,val,sa); \
        } else { \
          res = val >> sa; \
          SET_FLAGS(CF_SHR_BYTE,val,sa); \
        } \
      } else { \
        if (ra) { \
          if (!through_carry) { \
            res = (val >> ra) | (val << (8-ra)); \
            cf = (res & 0x80) != 0; \
          } else { \
            res = (val >> ra) | (GET_CARRY_FLAG_AS_BOOL << (8-ra)) | (val << (9-ra)); \
            cf = ((val >> (ra-1)) & 0x1) != 0; \
          } \
        } else { \
          res = val; \
          if (through_carry) { \
            cf = GET_CARRY_FLAG_AS_BOOL; \
          } else { \
            cf = (res & 0x80) != 0; \
          } \
        } \
        if (through_carry) { \
          SET_FLAGS_CF_OF(cf, (cf ^ ((res & 0x80) != 0))); \
        } else { \
          SET_FLAGS_CF_OF(cf, ((res & 0x80) != 0) ^ ((res & 0x40) != 0)); \
        } \
      } \
    } else { \
      res = val; \
    } \
    STORE_RM_BYTE(res); \
  }

#define SHIFT_R_WORD_OP(sa_source,is_rotate,through_carry,is_arithmetic,is_double) { \
    IVAL val, sa, ra, res, tmp; \
    IVAL cf; \
    sa = sa_source & 0x1F; \
    if (is_rotate) { \
      if (through_carry) { \
        ra = Mod17Table[sa]; \
      } else { \
        ra = sa & 0xF; \
      } \
    } \
    LOAD_RM_RW_WORD(val); \
    if (sa) { \
      if (!is_rotate) { \
        if (is_arithmetic) { \
          res = ((INT16)val) >> sa; \
          SET_FLAGS(CF_SAR_WORD,val,sa); \
        } else if (is_double) { \
          tmp = LOAD_REG_WORD; \
          res = (tmp << (16-sa)) | ((val >> sa) & ((1 << (16-sa)) - 1)); \
          SET_FLAGS(CF_SHR_WORD,val,sa); \
        } else { \
          res = val >> sa; \
          SET_FLAGS(CF_SHR_WORD,val,sa); \
        } \
      } else { \
        if (ra) { \
          if (!through_carry) { \
            res = (val >> ra) | (val << (16-ra)); \
            cf = (res & 0x8000) != 0; \
          } else { \
            res = (val >> ra) | (GET_CARRY_FLAG_AS_BOOL << (16-ra)) | (val << (17-ra)); \
            cf = ((val >> (ra-1)) & 0x1) != 0; \
          } \
        } else { \
          res = val; \
          if (through_carry) { \
            cf = GET_CARRY_FLAG_AS_BOOL; \
          } else { \
            cf = (res & 0x8000) != 0; \
          } \
        } \
        if (through_carry) { \
          SET_FLAGS_CF_OF(cf, (cf ^ ((res & 0x8000) != 0))); \
        } else { \
          SET_FLAGS_CF_OF(cf, ((res & 0x8000) != 0) ^ ((res & 0x4000) != 0)); \
        } \
      } \
    } else { \
      res = val; \
    } \
    STORE_RM_WORD(res); \
  }

// TODO: S,Z,P flags are not quite right for is_double (other sizes and left/right too)
#define SHIFT_R_DWORD_OP(sa_source,is_rotate,through_carry,is_arithmetic,is_double) { \
    IVAL val, sa, ra, res, tmp; \
    IVAL cf; \
    sa = sa_source & 0x1F; \
    ra = sa; \
    LOAD_RM_RW_DWORD(val); \
    if (sa) { \
      if (!is_rotate) { \
        if (is_arithmetic) { \
          res = ((INT32)val) >> sa; \
          SET_FLAGS(CF_SAR_DWORD,val,sa); \
        } else if (is_double) { \
          tmp = LOAD_REG_DWORD; \
          res = (tmp << (32-sa)) | ((val >> sa) & ((1 << (32-sa)) - 1)); \
          SET_FLAGS(CF_SHR_DWORD,val,sa); \
        } else { \
          res = val >> sa; \
          SET_FLAGS(CF_SHR_DWORD,val,sa); \
        } \
      } else { \
        if (!through_carry) { \
          res = (val >> ra) | (val << (32-ra)); \
          cf = (res & 0x80000000) != 0; \
        } else { \
          res = (val >> ra) | (GET_CARRY_FLAG_AS_BOOL << (32-ra)) | ((ra == 1) ? 0 : (val << (33-ra))); \
          cf = ((val >> (ra-1)) & 0x1) != 0; \
        } \
        if (through_carry) { \
          SET_FLAGS_CF_OF(cf, (cf ^ ((res & 0x80000000) != 0))); \
        } else { \
          SET_FLAGS_CF_OF(cf, ((res & 0x80000000) != 0) ^ ((res & 0x40000000) != 0)); \
        } \
      } \
    } else { \
      res = val; \
    } \
    STORE_RM_DWORD(res); \
  }

#define STRING_OP(sz, load_mem1, load_mem2, load_reg1, store_mem2, store_reg, do_cmp, load_io, store_io) { \
    IVAL src1, src2, ofs1, ofs2, ctr; \
    SVAL szadj; \
    if (X86_TMP(prefixes) & (X86_DEC_PREFIX_REP | X86_DEC_PREFIX_REPNE)) { \
      CONTROL_POINT; \
      ctr = LOAD_REG_ECX; \
      if (!ADDR_SZ) { \
        ctr = ctr & 0xFFFF; \
      } \
      if (ctr == 0) return; \
    } else { \
      ctr = 0; \
    } \
    if (load_mem1) { \
      ofs1 = LOAD_REG_ESI; \
      if (sz == 1) LOAD_MEM_BYTE(src1,ofs1); \
      if (sz == 2) LOAD_MEM_WORD(src1,ofs1); \
      if (sz == 4) LOAD_MEM_DWORD(src1,ofs1); \
    } \
    if (load_mem2) { \
      ofs2 = LOAD_REG_EDI; \
      SEG_OVERRIDE(ES); \
      if (sz == 1) LOAD_MEM_BYTE(src2,ofs2); \
      if (sz == 2) LOAD_MEM_WORD(src2,ofs2); \
      if (sz == 4) LOAD_MEM_DWORD(src2,ofs2); \
    } \
    if (load_reg1) { \
      src1 = LOAD_REG_EAX; \
    } \
    if (store_mem2) { \
      ofs2 = LOAD_REG_EDI; \
      SEG_OVERRIDE(ES); \
      if (sz == 1) STORE_MEM_BYTE(ofs2,src1); \
      if (sz == 2) STORE_MEM_WORD(ofs2,src1); \
      if (sz == 4) STORE_MEM_DWORD(ofs2,src1); \
    } \
    if (store_reg) { \
      if (sz == 1) STORE_REG_ID_BYTE(AL,src1); \
      if (sz == 2) STORE_REG_ID_WORD(AX,src1); \
      if (sz == 4) STORE_REG_ID_DWORD(EAX,src1); \
    } \
    if (do_cmp) { \
      if (sz == 1) SET_FLAGS(CF_SUB_BYTE, src1, src2); \
      if (sz == 2) SET_FLAGS(CF_SUB_WORD, src1, src2); \
      if (sz == 4) SET_FLAGS(CF_SUB_DWORD, src1, src2); \
    } \
    szadj = (GET_DIRECTION_FLAG) ? -(sz) : (sz); \
    if (load_mem1) { \
      ofs1 += szadj; \
      if (ADDR_SZ) { \
        STORE_REG_ID_DWORD(ESI,ofs1); \
      } else { \
        STORE_REG_ID_WORD(SI,ofs1); \
      } \
    } \
    if (load_mem2 || store_mem2) { \
      ofs2 += szadj; \
      if (ADDR_SZ) { \
        STORE_REG_ID_DWORD(EDI,ofs2); \
      } else { \
        STORE_REG_ID_WORD(DI,ofs2); \
      } \
    } \
    if (X86_TMP(prefixes) & (X86_DEC_PREFIX_REP | X86_DEC_PREFIX_REPNE)) { \
      ctr--; \
      if (ADDR_SZ) { \
        STORE_REG_ID_DWORD(ECX,ctr); \
      } else { \
        STORE_REG_ID_WORD(CX,ctr); \
      } \
      if (ctr && ((!do_cmp) || ((GET_ZERO_FLAG) ^ ((X86_TMP(prefixes) & X86_DEC_PREFIX_REPNE) != 0)))) { \
        NEXT_IP = PREV_IP; \
      } \
    } \
  }

#define PUSH_OP(getter, sz) {\
    IVAL val, ptr; \
    INT fStackSize; \
    fStackSize = GetSegAttr(SS,SEG_ATTR_B); \
    getter; \
    ptr = LOAD_REG_ESP; \
    SEG_OVERRIDE(SS); \
    if (sz) { \
      ptr -= 4; \
      ptr = fStackSize ? ptr : ptr & 0xFFFF; \
      STORE_MEM_DWORD(ptr,val); \
    } else { \
      ptr -= 2; \
      ptr = fStackSize ? ptr : ptr & 0xFFFF; \
      STORE_MEM_WORD(ptr,val); \
    } \
    if (fStackSize) { \
      STORE_REG_ID_DWORD(ESP,ptr); \
    } else { \
      STORE_REG_ID_WORD(SP, ptr); \
    } \
  }

#define POP_OP(setter, sz, suppress_stack_inc) {\
    IVAL val, ptr; \
    INT fStackSize; \
    INT savedOverride; \
    fStackSize = GetSegAttr(SS,SEG_ATTR_B); \
    savedOverride = X86_TMP(segOverride); \
    ptr = LOAD_REG_ESP; \
    if (!fStackSize) { \
      ptr = ptr & 0xFFFF; \
    } \
    SEG_OVERRIDE(SS); \
    if (sz) { \
      LOAD_MEM_DWORD(val, ptr); \
      ptr += 4; \
    } else { \
      LOAD_MEM_WORD(val,ptr); \
      ptr += 2; \
    } \
    SEG_OVERRIDE(savedOverride); \
    setter; \
    if (!suppress_stack_inc) { \
      if (fStackSize) { \
        STORE_REG_ID_DWORD(ESP,ptr); \
      } else { \
        STORE_REG_ID_WORD(SP, ptr); \
      } \
    } \
  }

#define LOAD_SEG_OFS_WORD_OP(reg) { \
    IVAL seg, ofs; \
    if (X86_TMP(mod) == 0xC0) { \
      INVALIDOP(); \
    } \
    ofs = LOAD_VIRTUAL_UNALIGNED_WORD(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ); \
    seg = LOAD_VIRTUAL_UNALIGNED_WORD(X86_TMP(va)+2,X86_TMP(segOverride),ACCESS_READ); \
    SET_SEG_SEL_ID(reg,seg); \
    STORE_REG_WORD(ofs); \
  }

#define LOAD_SEG_OFS_DWORD_OP(reg) { \
    IVAL seg, ofs; \
    if (X86_TMP(mod) == 0xC0) { \
      INVALIDOP(); \
    } \
    ofs = LOAD_VIRTUAL_UNALIGNED_DWORD(X86_TMP(va),X86_TMP(segOverride), ACCESS_READ); \
    seg = LOAD_VIRTUAL_UNALIGNED_WORD(X86_TMP(va)+4,X86_TMP(segOverride),ACCESS_READ); \
    SET_SEG_SEL_ID(reg,seg); \
    STORE_REG_DWORD(ofs); \
  }

#define BIT_TEST_WORD_OP(is_imm,post_op,lock_impl) { \
  IVAL base; \
  UINT bit; \
  IVAL addr; \
  if (LOCKED) { \
    lock_impl(); \
  } else { \
    if (X86_TMP(mod) == 0xC0) { \
      /* base is reg, so offset is unsigned */ \
      LOAD_RM_R_WORD(base); \
      if (is_imm) { \
        bit = (UINT)(LOAD_IMM & 0xF); \
      } else { \
        bit = (UINT)(LOAD_REG_WORD & 0xF); \
      } \
      addr = 0; \
    } else { \
      /* base is mem, so offset is signed, low order bits determine bit offset */ \
      IVAL disp; \
      if (is_imm) { \
        disp = LOAD_IMM & 0x1F; \
      } else { \
        disp = LOAD_REG_WORD; \
      } \
      bit = (UINT)(disp & 0x7); \
      INT16 ofs = ((INT16)disp)>>3; \
      addr = (X86_TMP(va) + ofs) & 0xFFFF; \
      LOAD_MEM_BYTE(base, addr); \
    } \
    UINT res = (base & (1 << bit)) ? EFLAGS_BIT_C : 0; \
    SET_FLAGS_EAGER(res); \
    switch (post_op) { \
      case 0: \
        /* do nothing */ \
        break; \
      case 1: \
        base |= (1 << bit); \
        goto bit_op_update; \
      case 2: \
        base &= ~(1 << bit); \
        goto bit_op_update; \
      case 3: \
        base ^= (1 << bit); \
        goto bit_op_update; \
      bit_op_update: \
        if (X86_TMP(mod) == 0xC0) { \
          STORE_RM_WORD(base); \
        } else { \
          STORE_MEM_BYTE(addr,base); \
        } \
        break; \
    } \
  } \
}

#define BIT_TEST_DWORD_OP(is_imm,post_op, lock_impl) { \
  IVAL base; \
  UINT bit; \
  IVAL addr; \
  if (LOCKED) { \
    lock_impl(); \
  } else { \
    if (X86_TMP(mod) == 0xC0) { \
      /* base is reg, so offset is unsigned */ \
      LOAD_RM_R_DWORD(base); \
      if (is_imm) { \
        bit = (UINT)(LOAD_IMM & 0x1F); \
      } else { \
        bit = (UINT)(LOAD_REG_DWORD & 0x1F); \
      } \
      addr = 0; \
    } else { \
      /* base is mem, so offset is signed, low order bits determine bit offset */ \
      IVAL disp; \
      if (is_imm) { \
        disp = LOAD_IMM & 0x1F; \
      } else { \
        disp = LOAD_REG_DWORD; \
      } \
      bit = (UINT)(disp & 0x7); \
      INT32 ofs = ((INT32)disp)>>3; \
      addr = (X86_TMP(va) + ofs) & IC(0xFFFFFFFF); \
      LOAD_MEM_BYTE(base, addr); \
    } \
    UINT res = (base & (1 << bit)) ? EFLAGS_BIT_C : 0; \
    SET_FLAGS_EAGER(res); \
    switch (post_op) { \
      case 0: \
        /* do nothing */ \
        break; \
      case 1: \
        base |= (1 << bit); \
        goto bit_op_update; \
      case 2: \
        base &= ~(1 << bit); \
        goto bit_op_update; \
      case 3: \
        base ^= (1 << bit); \
        goto bit_op_update; \
      bit_op_update: \
        if (X86_TMP(mod) == 0xC0) { \
          STORE_RM_DWORD(base); \
        } else { \
          STORE_MEM_BYTE(addr,base); \
        } \
        break; \
    } \
  } \
}


void ADD_Eb_Gb()    { BINARY_EB_GB_OP(+,CF_ADD_BYTE,0,0,LockAddB); }
void ADD_Ew_Gw()    { BINARY_EW_GW_OP(+,CF_ADD_WORD,0,0,LockAddH); }
void ADD_Ed_Gd()    { BINARY_ED_GD_OP(+,CF_ADD_DWORD,0,0,LockAddW); }
void ADD_Gb_Eb()    { BINARY_GB_EB_OP(+,CF_ADD_BYTE,0,0); }
void ADD_Gw_Ew()    { BINARY_GW_EW_OP(+,CF_ADD_WORD,0,0); }
void ADD_Gd_Ed()    { BINARY_GD_ED_OP(+,CF_ADD_DWORD,0,0); }
void ADD_AL_Ib()    { BINARY_AL_IB_OP(+,CF_ADD_BYTE,0,0); }
void ADD_AX_Iw()    { BINARY_AX_IW_OP(+,CF_ADD_WORD,0,0); }
void ADD_EAX_Id()   { BINARY_EAX_ID_OP(+,CF_ADD_DWORD,0,0); }
void ADD_Eb_I()     { BINARY_EB_I_OP(+,CF_ADD_BYTE,0,0,LockAddB); }
void ADD_Ew_I()     { BINARY_EW_I_OP(+,CF_ADD_WORD,0,0,LockAddH); }
void ADD_Ed_I()     { BINARY_ED_I_OP(+,CF_ADD_DWORD,0,0,LockAddW); }

void OR_Eb_Gb()     { BINARY_EB_GB_OP(|,CF_OR_BYTE,0,0,LockOrB); }
void OR_Ew_Gw()     { BINARY_EW_GW_OP(|,CF_OR_WORD,0,0,LockOrH); }
void OR_Ed_Gd()     { BINARY_ED_GD_OP(|,CF_OR_DWORD,0,0,LockOrW); }
void OR_Gb_Eb()     { BINARY_GB_EB_OP(|,CF_OR_BYTE,0,0); }
void OR_Gw_Ew()     { BINARY_GW_EW_OP(|,CF_OR_WORD,0,0); }
void OR_Gd_Ed()     { BINARY_GD_ED_OP(|,CF_OR_DWORD,0,0); }
void OR_AL_Ib()     { BINARY_AL_IB_OP(|,CF_OR_BYTE,0,0); }
void OR_AX_Iw()     { BINARY_AX_IW_OP(|,CF_OR_WORD,0,0); }
void OR_EAX_Id()    { BINARY_EAX_ID_OP(|,CF_OR_DWORD,0,0); }
void OR_Eb_I()      { BINARY_EB_I_OP(|,CF_OR_BYTE,0,0,LockOrB); }
void OR_Ew_I()      { BINARY_EW_I_OP(|,CF_OR_WORD,0,0,LockOrH); }
void OR_Ed_I()      { BINARY_ED_I_OP(|,CF_OR_DWORD,0,0,LockOrW); }

void ADC_Eb_Gb()    { BINARY_EB_GB_OP(+,CF_ADC_BYTE,1,0,LockAdcB); }
void ADC_Ew_Gw()    { BINARY_EW_GW_OP(+,CF_ADC_WORD,1,0,LockAdcH); }
void ADC_Ed_Gd()    { BINARY_ED_GD_OP(+,CF_ADC_DWORD,1,0,LockAdcW); }
void ADC_Gb_Eb()    { BINARY_GB_EB_OP(+,CF_ADC_BYTE,1,0); }
void ADC_Gw_Ew()    { BINARY_GW_EW_OP(+,CF_ADC_WORD,1,0); }
void ADC_Gd_Ed()    { BINARY_GD_ED_OP(+,CF_ADC_DWORD,1,0); }
void ADC_AL_Ib()    { BINARY_AL_IB_OP(+,CF_ADC_BYTE,1,0); }
void ADC_AX_Iw()    { BINARY_AX_IW_OP(+,CF_ADC_WORD,1,0); }
void ADC_EAX_Id()   { BINARY_EAX_ID_OP(+,CF_ADC_DWORD,1,0); }
void ADC_Eb_I()     { BINARY_EB_I_OP(+,CF_ADC_BYTE,1,0,LockAdcB); }
void ADC_Ew_I()     { BINARY_EW_I_OP(+,CF_ADC_WORD,1,0,LockAdcH); }
void ADC_Ed_I()     { BINARY_ED_I_OP(+,CF_ADC_DWORD,1,0,LockAdcW); }

void SBB_Eb_Gb()    { BINARY_EB_GB_OP(-,CF_SBB_BYTE,1,0,LockSbbB); }
void SBB_Ew_Gw()    { BINARY_EW_GW_OP(-,CF_SBB_WORD,1,0,LockSbbH); }
void SBB_Ed_Gd()    { BINARY_ED_GD_OP(-,CF_SBB_DWORD,1,0,LockSbbW); }
void SBB_Gb_Eb()    { BINARY_GB_EB_OP(-,CF_SBB_BYTE,1,0); }
void SBB_Gw_Ew()    { BINARY_GW_EW_OP(-,CF_SBB_WORD,1,0); }
void SBB_Gd_Ed()    { BINARY_GD_ED_OP(-,CF_SBB_DWORD,1,0); }
void SBB_AL_Ib()    { BINARY_AL_IB_OP(-,CF_SBB_BYTE,1,0); }
void SBB_AX_Iw()    { BINARY_AX_IW_OP(-,CF_SBB_WORD,1,0); }
void SBB_EAX_Id()   { BINARY_EAX_ID_OP(-,CF_SBB_DWORD,1,0); }
void SBB_Eb_I()     { BINARY_EB_I_OP(-,CF_SBB_BYTE,1,0,LockSbbB); }
void SBB_Ew_I()     { BINARY_EW_I_OP(-,CF_SBB_WORD,1,0,LockSbbH); }
void SBB_Ed_I()     { BINARY_ED_I_OP(-,CF_SBB_DWORD,1,0,LockSbbW); }

void AND_Eb_Gb()    { BINARY_EB_GB_OP(&,CF_AND_BYTE,0,0, LockAndB); }
void AND_Ew_Gw()    { BINARY_EW_GW_OP(&,CF_AND_WORD,0,0, LockAndH); }
void AND_Ed_Gd()    { BINARY_ED_GD_OP(&,CF_AND_DWORD,0,0, LockAndW); }
void AND_Gb_Eb()    { BINARY_GB_EB_OP(&,CF_AND_BYTE,0,0); }
void AND_Gw_Ew()    { BINARY_GW_EW_OP(&,CF_AND_WORD,0,0); }
void AND_Gd_Ed()    { BINARY_GD_ED_OP(&,CF_AND_DWORD,0,0); }
void AND_AL_Ib()    { BINARY_AL_IB_OP(&,CF_AND_BYTE,0,0); }
void AND_AX_Iw()    { BINARY_AX_IW_OP(&,CF_AND_WORD,0,0); }
void AND_EAX_Id()   { BINARY_EAX_ID_OP(&,CF_AND_DWORD,0,0); }
void AND_Eb_I()     { BINARY_EB_I_OP(&,CF_AND_BYTE,0,0, LockAndB); }
void AND_Ew_I()     { BINARY_EW_I_OP(&,CF_AND_WORD,0,0, LockAndH); }
void AND_Ed_I()     { BINARY_ED_I_OP(&,CF_AND_DWORD,0,0, LockAndW); }

void SUB_Eb_Gb()    { BINARY_EB_GB_OP(-,CF_SUB_BYTE,0,0, LockSubB); }
void SUB_Ew_Gw()    { BINARY_EW_GW_OP(-,CF_SUB_WORD,0,0, LockSubH); }
void SUB_Ed_Gd()    { BINARY_ED_GD_OP(-,CF_SUB_DWORD,0,0, LockSubW); }
void SUB_Gb_Eb()    { BINARY_GB_EB_OP(-,CF_SUB_BYTE,0,0); }
void SUB_Gw_Ew()    { BINARY_GW_EW_OP(-,CF_SUB_WORD,0,0); }
void SUB_Gd_Ed()    { BINARY_GD_ED_OP(-,CF_SUB_DWORD,0,0); }
void SUB_AL_Ib()    { BINARY_AL_IB_OP(-,CF_SUB_BYTE,0,0); }
void SUB_AX_Iw()    { BINARY_AX_IW_OP(-,CF_SUB_WORD,0,0); }
void SUB_EAX_Id()   { BINARY_EAX_ID_OP(-,CF_SUB_DWORD,0,0); }
void SUB_Eb_I()     { BINARY_EB_I_OP(-,CF_SUB_BYTE,0,0, LockSubB); }
void SUB_Ew_I()     { BINARY_EW_I_OP(-,CF_SUB_WORD,0,0, LockSubH); }
void SUB_Ed_I()     { BINARY_ED_I_OP(-,CF_SUB_DWORD,0,0, LockSubW); }

void XOR_Eb_Gb()    { BINARY_EB_GB_OP(^,CF_XOR_BYTE,0,0,LockXorB); }
void XOR_Ew_Gw()    { BINARY_EW_GW_OP(^,CF_XOR_WORD,0,0,LockXorH); }
void XOR_Ed_Gd()    { BINARY_ED_GD_OP(^,CF_XOR_DWORD,0,0,LockXorW); }
void XOR_Gb_Eb()    { BINARY_GB_EB_OP(^,CF_XOR_BYTE,0,0); }
void XOR_Gw_Ew()    { BINARY_GW_EW_OP(^,CF_XOR_WORD,0,0); }
void XOR_Gd_Ed()    { BINARY_GD_ED_OP(^,CF_XOR_DWORD,0,0); }
void XOR_AL_Ib()    { BINARY_AL_IB_OP(^,CF_XOR_BYTE,0,0); }
void XOR_AX_Iw()    { BINARY_AX_IW_OP(^,CF_XOR_WORD,0,0); }
void XOR_EAX_Id()   { BINARY_EAX_ID_OP(^,CF_XOR_DWORD,0,0); }
void XOR_Eb_I()     { BINARY_EB_I_OP(^,CF_XOR_BYTE,0,0,LockXorB); }
void XOR_Ew_I()     { BINARY_EW_I_OP(^,CF_XOR_WORD,0,0,LockXorH); }
void XOR_Ed_I()     { BINARY_ED_I_OP(^,CF_XOR_DWORD,0,0,LockXorW); }

void CMP_Eb_Gb()    { BINARY_EB_GB_OP(-,CF_SUB_BYTE,0,1,LockInvalid); }
void CMP_Ew_Gw()    { BINARY_EW_GW_OP(-,CF_SUB_WORD,0,1,LockInvalid); }
void CMP_Ed_Gd()    { BINARY_ED_GD_OP(-,CF_SUB_DWORD,0,1,LockInvalid); }
void CMP_Gb_Eb()    { BINARY_GB_EB_OP(-,CF_SUB_BYTE,0,1); }
void CMP_Gw_Ew()    { BINARY_GW_EW_OP(-,CF_SUB_WORD,0,1); }
void CMP_Gd_Ed()    { BINARY_GD_ED_OP(-,CF_SUB_DWORD,0,1); }
void CMP_AL_Ib()    { BINARY_AL_IB_OP(-,CF_SUB_BYTE,0,1); }
void CMP_AX_Iw()    { BINARY_AX_IW_OP(-,CF_SUB_WORD,0,1); }
void CMP_EAX_Id()   { BINARY_EAX_ID_OP(-,CF_SUB_DWORD,0,1); }
void CMP_Eb_I()     { BINARY_EB_I_OP(-,CF_SUB_BYTE,0,1,LockInvalid); }
void CMP_Ew_I()     { BINARY_EW_I_OP(-,CF_SUB_WORD,0,1,LockInvalid); }
void CMP_Ed_I()     { BINARY_ED_I_OP(-,CF_SUB_DWORD,0,1,LockInvalid); }

void TEST_Eb_Gb()    { BINARY_EB_GB_OP(&,CF_AND_BYTE,0,1,LockInvalid); }
void TEST_Ew_Gw()    { BINARY_EW_GW_OP(&,CF_AND_WORD,0,1,LockInvalid); }
void TEST_Ed_Gd()    { BINARY_ED_GD_OP(&,CF_AND_DWORD,0,1,LockInvalid); }
void TEST_AL_Ib()    { BINARY_AL_IB_OP(&,CF_AND_BYTE,0,1); }
void TEST_AX_Iw()    { BINARY_AX_IW_OP(&,CF_AND_WORD,0,1); }
void TEST_EAX_Id()   { BINARY_EAX_ID_OP(&,CF_AND_DWORD,0,1); }
void TEST_Eb_I()     { BINARY_EB_I_OP(&,CF_AND_BYTE,0,1,LockInvalid); }
void TEST_Ew_I()     { BINARY_EW_I_OP(&,CF_AND_WORD,0,1,LockInvalid); }
void TEST_Ed_I()     { BINARY_ED_I_OP(&,CF_AND_DWORD,0,1,LockInvalid); }

void INC_AX() { REFLEXIVE_REG_WORD_OP(+,AX,1,CF_INC_WORD); }
void INC_CX() { REFLEXIVE_REG_WORD_OP(+,CX,1,CF_INC_WORD); }
void INC_DX() { REFLEXIVE_REG_WORD_OP(+,DX,1,CF_INC_WORD); }
void INC_BX() { REFLEXIVE_REG_WORD_OP(+,BX,1,CF_INC_WORD); }
void INC_SP() { REFLEXIVE_REG_WORD_OP(+,SP,1,CF_INC_WORD); }
void INC_BP() { REFLEXIVE_REG_WORD_OP(+,BP,1,CF_INC_WORD); }
void INC_SI() { REFLEXIVE_REG_WORD_OP(+,SI,1,CF_INC_WORD); }
void INC_DI() { REFLEXIVE_REG_WORD_OP(+,DI,1,CF_INC_WORD); }
void INC_EAX() { REFLEXIVE_REG_DWORD_OP(+,EAX,1,CF_INC_DWORD); }
void INC_ECX() { REFLEXIVE_REG_DWORD_OP(+,ECX,1,CF_INC_DWORD); }
void INC_EDX() { REFLEXIVE_REG_DWORD_OP(+,EDX,1,CF_INC_DWORD); }
void INC_EBX() { REFLEXIVE_REG_DWORD_OP(+,EBX,1,CF_INC_DWORD); }
void INC_ESP() { REFLEXIVE_REG_DWORD_OP(+,ESP,1,CF_INC_DWORD); }
void INC_EBP() { REFLEXIVE_REG_DWORD_OP(+,EBP,1,CF_INC_DWORD); }
void INC_ESI() { REFLEXIVE_REG_DWORD_OP(+,ESI,1,CF_INC_DWORD); }
void INC_EDI() { REFLEXIVE_REG_DWORD_OP(+,EDI,1,CF_INC_DWORD); }
void INC_Eb()  { REFLEXIVE_EB_OP(+,1,CF_INC_BYTE,LockIncB); }
void INC_Ew()  { REFLEXIVE_EW_OP(+,1,CF_INC_WORD,LockIncH); }
void INC_Ed()  { REFLEXIVE_ED_OP(+,1,CF_INC_DWORD,LockIncW); }

void DEC_AX() { REFLEXIVE_REG_WORD_OP(-,AX,1,CF_DEC_WORD); }
void DEC_CX() { REFLEXIVE_REG_WORD_OP(-,CX,1,CF_DEC_WORD); }
void DEC_DX() { REFLEXIVE_REG_WORD_OP(-,DX,1,CF_DEC_WORD); }
void DEC_BX() { REFLEXIVE_REG_WORD_OP(-,BX,1,CF_DEC_WORD); }
void DEC_SP() { REFLEXIVE_REG_WORD_OP(-,SP,1,CF_DEC_WORD); }
void DEC_BP() { REFLEXIVE_REG_WORD_OP(-,BP,1,CF_DEC_WORD); }
void DEC_SI() { REFLEXIVE_REG_WORD_OP(-,SI,1,CF_DEC_WORD); }
void DEC_DI() { REFLEXIVE_REG_WORD_OP(-,DI,1,CF_DEC_WORD); }
void DEC_EAX() { REFLEXIVE_REG_DWORD_OP(-,EAX,1,CF_DEC_DWORD); }
void DEC_ECX() { REFLEXIVE_REG_DWORD_OP(-,ECX,1,CF_DEC_DWORD); }
void DEC_EDX() { REFLEXIVE_REG_DWORD_OP(-,EDX,1,CF_DEC_DWORD); }
void DEC_EBX() { REFLEXIVE_REG_DWORD_OP(-,EBX,1,CF_DEC_DWORD); }
void DEC_ESP() { REFLEXIVE_REG_DWORD_OP(-,ESP,1,CF_DEC_DWORD); }
void DEC_EBP() { REFLEXIVE_REG_DWORD_OP(-,EBP,1,CF_DEC_DWORD); }
void DEC_ESI() { REFLEXIVE_REG_DWORD_OP(-,ESI,1,CF_DEC_DWORD); }
void DEC_EDI() { REFLEXIVE_REG_DWORD_OP(-,EDI,1,CF_DEC_DWORD); }
void DEC_Eb()  { REFLEXIVE_EB_OP(-,1,CF_DEC_BYTE,LockDecB); }
void DEC_Ew()  { REFLEXIVE_EW_OP(-,1,CF_DEC_WORD,LockDecH); }
void DEC_Ed()  { REFLEXIVE_ED_OP(-,1,CF_DEC_DWORD,LockDecW); }

void NOT_Eb() { UNARY_EB_OP(~,CF_INVALID,0,LockNotB); }
void NOT_Ew() { UNARY_EW_OP(~,CF_INVALID,0,LockNotH); }
void NOT_Ed() { UNARY_ED_OP(~,CF_INVALID,0,LockNotW); }

void NEG_Eb() { UNARY_EB_OP(-,CF_NEG_BYTE,1,LockNegB); }
void NEG_Ew() { UNARY_EW_OP(-,CF_NEG_WORD,1,LockNegH); }
void NEG_Ed() { UNARY_ED_OP(-,CF_NEG_DWORD,1,LockNegW); }

void CMOVO_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_O); }
void CMOVO_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_O); }
void CMOVNO_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_O)); }
void CMOVNO_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_O)); }
void CMOVB_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_B); }
void CMOVB_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_B); }
void CMOVAE_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_B)); }
void CMOVAE_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_B)); }
void CMOVE_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_E); }
void CMOVE_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_E); }
void CMOVNE_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_E)); }
void CMOVNE_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_E)); }
void CMOVBE_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_BE); }
void CMOVBE_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_BE); }
void CMOVA_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_BE)); }
void CMOVA_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_BE)); }
void CMOVS_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_S); }
void CMOVS_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_S); }
void CMOVNS_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_S)); }
void CMOVNS_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_S)); }
void CMOVP_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_P); }
void CMOVP_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_P); }
void CMOVNP_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_P)); }
void CMOVNP_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_P)); }
void CMOVL_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_L); }
void CMOVL_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_L); }
void CMOVNL_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_L)); }
void CMOVNL_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_L)); }
void CMOVLE_Gw_Ew() { CMOV_GW_EW_OP(COND_EVAL_LE); }
void CMOVLE_Gd_Ed() { CMOV_GD_ED_OP(COND_EVAL_LE); }
void CMOVNLE_Gw_Ew() { CMOV_GW_EW_OP(!(COND_EVAL_LE)); }
void CMOVNLE_Gd_Ed() { CMOV_GD_ED_OP(!(COND_EVAL_LE)); }

void SETO_Eb() { SETCC_EB_OP(COND_EVAL_O); }
void SETNO_Eb() { SETCC_EB_OP(!(COND_EVAL_O)); }
void SETB_Eb() { SETCC_EB_OP(COND_EVAL_B); }
void SETAE_Eb() { SETCC_EB_OP(!(COND_EVAL_B)); }
void SETE_Eb() { SETCC_EB_OP(COND_EVAL_E); }
void SETNE_Eb() { SETCC_EB_OP(!(COND_EVAL_E)); }
void SETBE_Eb() { SETCC_EB_OP(COND_EVAL_BE); }
void SETA_Eb() { SETCC_EB_OP(!(COND_EVAL_BE)); }
void SETS_Eb() { SETCC_EB_OP(COND_EVAL_S); }
void SETNS_Eb() { SETCC_EB_OP(!(COND_EVAL_S)); }
void SETP_Eb() { SETCC_EB_OP(COND_EVAL_P); }
void SETNP_Eb() { SETCC_EB_OP(!(COND_EVAL_P)); }
void SETL_Eb() { SETCC_EB_OP(COND_EVAL_L); }
void SETNL_Eb() { SETCC_EB_OP(!(COND_EVAL_L)); }
void SETLE_Eb() { SETCC_EB_OP(COND_EVAL_LE); }
void SETNLE_Eb() { SETCC_EB_OP(!(COND_EVAL_LE)); }

void MOV_AL_Ib() { MOV_REG_IB_OP(AL); }
void MOV_CL_Ib() { MOV_REG_IB_OP(CL); }
void MOV_DL_Ib() { MOV_REG_IB_OP(DL); }
void MOV_BL_Ib() { MOV_REG_IB_OP(BL); }
void MOV_AH_Ib() { MOV_REG_IB_OP(AH); }
void MOV_CH_Ib() { MOV_REG_IB_OP(CH); }
void MOV_DH_Ib() { MOV_REG_IB_OP(DH); }
void MOV_BH_Ib() { MOV_REG_IB_OP(BH); }
void MOV_AX_Iw() { MOV_REG_IW_OP(AX); }
void MOV_CX_Iw() { MOV_REG_IW_OP(CX); }
void MOV_DX_Iw() { MOV_REG_IW_OP(DX); }
void MOV_BX_Iw() { MOV_REG_IW_OP(BX); }
void MOV_SP_Iw() { MOV_REG_IW_OP(SP); }
void MOV_BP_Iw() { MOV_REG_IW_OP(BP); }
void MOV_SI_Iw() { MOV_REG_IW_OP(SI); }
void MOV_DI_Iw() { MOV_REG_IW_OP(DI); }
void MOV_EAX_Id() { MOV_REG_ID_OP(EAX); }
void MOV_ECX_Id() { MOV_REG_ID_OP(ECX); }
void MOV_EDX_Id() { MOV_REG_ID_OP(EDX); }
void MOV_EBX_Id() { MOV_REG_ID_OP(EBX); }
void MOV_ESP_Id() { MOV_REG_ID_OP(ESP); }
void MOV_EBP_Id() { MOV_REG_ID_OP(EBP); }
void MOV_ESI_Id() { MOV_REG_ID_OP(ESI); }
void MOV_EDI_Id() { MOV_REG_ID_OP(EDI); }

void MOV_Eb_Gb() { IVAL val; val = LOAD_REG_BYTE; STORE_RM_W_BYTE(val); }
void MOV_Ew_Gw() { IVAL val; val = LOAD_REG_WORD; STORE_RM_W_WORD(val); }
void MOV_Ed_Gd() { IVAL val; val = LOAD_REG_DWORD; STORE_RM_W_DWORD(val); }
void MOV_Gb_Eb() { IVAL val; LOAD_RM_RW_BYTE(val); STORE_REG_BYTE(val); }
void MOV_Gw_Ew() { IVAL val; LOAD_RM_RW_WORD(val); STORE_REG_WORD(val); }
void MOV_Gd_Ed() { IVAL val; LOAD_RM_RW_DWORD(val); STORE_REG_DWORD(val); }
void MOV_Eb_Ib() { IVAL val; val = LOAD_IMM; STORE_RM_W_BYTE(val); }
void MOV_Ew_Iw() { IVAL val; val = LOAD_IMM; STORE_RM_W_WORD(val); }
void MOV_Ed_Id() { IVAL val; val = LOAD_IMM; STORE_RM_W_DWORD(val); }

void MOVZX_Gw_Eb() { IVAL val; LOAD_RM_RW_BYTE(val); STORE_REG_WORD(val); }
void MOVZX_Gd_Eb() { IVAL val; LOAD_RM_RW_BYTE(val); STORE_REG_DWORD(val); }
void MOVZX_Gd_Ew() { IVAL val; LOAD_RM_RW_WORD(val); STORE_REG_DWORD(val); }

void MOVSX_Gw_Eb() { IVAL val; LOAD_RM_RW_BYTE(val); val = (SVAL)(INT8)val; STORE_REG_WORD(val); }
void MOVSX_Gd_Eb() { IVAL val; LOAD_RM_RW_BYTE(val); val = (SVAL)(INT8)val; STORE_REG_DWORD(val); }
void MOVSX_Gd_Ew() { IVAL val; LOAD_RM_RW_WORD(val); val = (SVAL)(INT16)val; STORE_REG_DWORD(val); }

void CBW()  { IVAL val; val = LOAD_REG_EAX; val = (SVAL)(INT8)val; STORE_REG_ID_WORD(AX,val); }
void CWDE() { IVAL val; val = LOAD_REG_EAX; val = (SVAL)(INT16)val; STORE_REG_ID_DWORD(EAX,val); }
void CWD() { 
  IVAL val,hi; 
  val = LOAD_REG_EAX; 
  val = (SVAL)(INT16)val;
  hi = val >> 16;
  STORE_REG_ID_WORD(AX,val); 
  STORE_REG_ID_WORD(DX,hi);
}
void CDQ() {
  IVAL val, res;
  val = LOAD_REG_EAX;
  res = (val & 0x80000000) ? 0xFFFFFFFF : 0;
  STORE_REG_ID_DWORD(EDX,res);
}

void BSWAP_EAX() { BSWAP_REG_OP(EAX); }
void BSWAP_ECX() { BSWAP_REG_OP(ECX); }
void BSWAP_EDX() { BSWAP_REG_OP(EDX); }
void BSWAP_EBX() { BSWAP_REG_OP(EBX); }
void BSWAP_ESP() { BSWAP_REG_OP(ESP); }
void BSWAP_EBP() { BSWAP_REG_OP(EBP); }
void BSWAP_ESI() { BSWAP_REG_OP(ESI); }
void BSWAP_EDI() { BSWAP_REG_OP(EDI); }

void NOP() {
  // TODO: PAUSE == REP NOP
}

void XCHG_CX() { XCHG_REG_WORD_OP(CX); }
void XCHG_DX() { XCHG_REG_WORD_OP(DX); }
void XCHG_BX() { XCHG_REG_WORD_OP(BX); }
void XCHG_SP() { XCHG_REG_WORD_OP(SP); }
void XCHG_BP() { XCHG_REG_WORD_OP(BP); }
void XCHG_SI() { XCHG_REG_WORD_OP(SI); }
void XCHG_DI() { XCHG_REG_WORD_OP(DI); }
void XCHG_ECX() { XCHG_REG_DWORD_OP(ECX); }
void XCHG_EDX() { XCHG_REG_DWORD_OP(EDX); }
void XCHG_EBX() { XCHG_REG_DWORD_OP(EBX); }
void XCHG_ESP() { XCHG_REG_DWORD_OP(ESP); }
void XCHG_EBP() { XCHG_REG_DWORD_OP(EBP); }
void XCHG_ESI() { XCHG_REG_DWORD_OP(ESI); }
void XCHG_EDI() { XCHG_REG_DWORD_OP(EDI); }

void XCHG_Eb_Gb() {
  IVAL dst, src;
  // LockXchgB();
  bool repeat = curPE->P->isRepeating(); 
  curPE->lockMemory();
  LOAD_RM_RW_BYTE(dst);
  src = LOAD_REG_BYTE;
  if (!repeat) {
      curPE->instrs_executed=false;
      longjmp(curPE->memoryJumpEnv, 1);
  }
  STORE_RM_BYTE(src);
  STORE_REG_BYTE(dst);
  curPE->unlockMemory();
}
void XCHG_Ew_Gw() {
  IVAL dst, src;
  // LockXchgH();
  curPE->lockMemory();
  LOAD_RM_RW_WORD(dst);
  src = LOAD_REG_WORD;
  STORE_RM_WORD(src);
  STORE_REG_WORD(dst);
  curPE->unlockMemory();
}
void XCHG_Ed_Gd() {
  IVAL dst, src;
  // LockXchgW();
  curPE->lockMemory();
  LOAD_RM_RW_DWORD(dst);
  src = LOAD_REG_DWORD;
  STORE_RM_DWORD(src);
  STORE_REG_DWORD(dst);
  curPE->unlockMemory();
}

void XADD_Eb_Gb() {
  IVAL dst,src,res;
  if (LOCKED) {
    curPE->lockMemory();
  } 
  LOAD_RM_RW_BYTE(dst);
  src = LOAD_REG_BYTE;
  res = dst + src;
  STORE_RM_BYTE(res);
  STORE_REG_BYTE(dst);
  SET_FLAGS(CF_ADD_BYTE,dst,src);
  if (LOCKED) {
    curPE->unlockMemory();
  }
}
void XADD_Ew_Gw() {
  IVAL dst,src,res;
  if (LOCKED) {
    curPE->lockMemory();
  } 
  LOAD_RM_RW_WORD(dst);
  src = LOAD_REG_WORD;
  res = dst + src;
  STORE_RM_WORD(res);
  STORE_REG_WORD(dst);
  SET_FLAGS(CF_ADD_WORD,dst,src);
  if (LOCKED) {
    curPE->unlockMemory();
  }
}
void XADD_Ed_Gd() {
  IVAL dst,src,res;
  if (LOCKED) {
    curPE->lockMemory();
  } 
  LOAD_RM_RW_DWORD(dst);
  src = LOAD_REG_DWORD;
  res = dst + src;
  STORE_RM_DWORD(res);
  STORE_REG_DWORD(dst);
  SET_FLAGS(CF_ADD_DWORD,dst,src);
  if (LOCKED) {
    curPE->unlockMemory();
  }
}

void CMPXCHG_Eb_Gb() {
  IVAL dst,src,reg,res;
  if (LOCKED) {
    LockCmpXchgB();
  } else {
    LOAD_RM_RW_BYTE(dst);
    src = LOAD_REG_BYTE;
    reg = LOAD_REG_EAX & 0xFF;
    if (dst == reg) {
      res = src;
    } else {
      res = dst;
      STORE_REG_ID_BYTE(AL,dst);
    }
    STORE_RM_BYTE(res);
    SET_FLAGS(CF_SUB_BYTE,dst,reg);
  }
}

void CMPXCHG_Ew_Gw() {
  IVAL dst,src,reg,res;
  if (LOCKED) {
    LockCmpXchgH();
  } else {
    LOAD_RM_RW_WORD(dst);
    src = LOAD_REG_WORD;
    reg = LOAD_REG_EAX & 0xFFFF;
    if (dst == reg) {
      res = src;
    } else {
      res = dst;
      STORE_REG_ID_WORD(AX,dst);
    }
    STORE_RM_WORD(res);
    SET_FLAGS(CF_SUB_WORD,dst,reg);
  }
}

void CMPXCHG_Ed_Gd() {
    IVAL dst,src,reg,res;
    if (LOCKED) {
        // LockCmpXchgW();
        curPE->lockMemory();
    }
    LOAD_RM_RW_DWORD(dst);
    src = LOAD_REG_DWORD;
    reg = LOAD_REG_EAX & IC(0xFFFFFFFF);
    if (dst == reg) {
        res = src;
    } else {
        res = dst;
        STORE_REG_ID_DWORD(EAX,dst);
    }
    STORE_RM_DWORD(res);
    SET_FLAGS(CF_SUB_DWORD,dst,reg);
    if (LOCKED) {
        curPE->unlockMemory();
    }
}

// TODO: locking
void CMPXCH8B_Mq() {
  UINT64 m, da, cb;
  IVAL hi, lo;
  CONTROL_POINT; // TODO: better way of doing this

  // in general, it is not correct not to lock the bus for this method.
  // for pin, it is fine.
#ifndef WITH_PIN_SUPPORT  
  if (LOCKED) {
    LockCmpXchgD();
  } else {
#endif
    LOAD_RM_RW_QWORD(m);
    da = (((UINT64)(LOAD_REG_EDX)) << 32) | (((UINT64)(LOAD_REG_EAX)) & IC(0xFFFFFFFF));
    if (m == da) {
      SET_FLAGS_ZF(1);
      cb = (((UINT64)(LOAD_REG_ECX)) << 32) | (((UINT64)(LOAD_REG_EBX)) & IC(0xFFFFFFFF));
      STORE_RM_QWORD(cb);
    } else {
      SET_FLAGS_ZF(0);
      hi = m >> 32;
      lo = m;
      STORE_RM_QWORD(da); // always a write cycle regardless of result
      STORE_REG_ID_DWORD(EDX, hi);
      STORE_REG_ID_DWORD(EAX, lo);
    }
#ifndef WITH_PIN_SUPPORT  
  }
#else

#endif // WITH_PIN_SUPPORT
}


void ROL_Eb_Ib()     { SHIFT_L_BYTE_OP(LOAD_IMM    ,1,0,0,0); }
void ROR_Eb_Ib()     { SHIFT_R_BYTE_OP(LOAD_IMM    ,1,0,0,0); }
void RCL_Eb_Ib()     { SHIFT_L_BYTE_OP(LOAD_IMM    ,1,1,0,0); }
void RCR_Eb_Ib()     { SHIFT_R_BYTE_OP(LOAD_IMM    ,1,1,0,0); }
void SHL_Eb_Ib()     { SHIFT_L_BYTE_OP(LOAD_IMM    ,0,0,0,0); }
void SHR_Eb_Ib()     { SHIFT_R_BYTE_OP(LOAD_IMM    ,0,0,0,0); }
void SAR_Eb_Ib()     { SHIFT_R_BYTE_OP(LOAD_IMM    ,0,0,1,0); }
void ROL_Eb_1()      { SHIFT_L_BYTE_OP(1           ,1,0,0,0); }
void ROR_Eb_1()      { SHIFT_R_BYTE_OP(1           ,1,0,0,0); }
void RCL_Eb_1()      { SHIFT_L_BYTE_OP(1           ,1,1,0,0); }
void RCR_Eb_1()      { SHIFT_R_BYTE_OP(1           ,1,1,0,0); }
void SHL_Eb_1()      { SHIFT_L_BYTE_OP(1           ,0,0,0,0); }
void SHR_Eb_1()      { SHIFT_R_BYTE_OP(1           ,0,0,0,0); }
void SAR_Eb_1()      { SHIFT_R_BYTE_OP(1           ,0,0,1,0); }
void ROL_Eb_CL()     { SHIFT_L_BYTE_OP(LOAD_REG_ECX,1,0,0,0); }
void ROR_Eb_CL()     { SHIFT_R_BYTE_OP(LOAD_REG_ECX,1,0,0,0); }
void RCL_Eb_CL()     { SHIFT_L_BYTE_OP(LOAD_REG_ECX,1,1,0,0); }
void RCR_Eb_CL()     { SHIFT_R_BYTE_OP(LOAD_REG_ECX,1,1,0,0); }
void SHL_Eb_CL()     { SHIFT_L_BYTE_OP(LOAD_REG_ECX,0,0,0,0); }
void SHR_Eb_CL()     { SHIFT_R_BYTE_OP(LOAD_REG_ECX,0,0,0,0); }
void SAR_Eb_CL()     { SHIFT_R_BYTE_OP(LOAD_REG_ECX,0,0,1,0); }
void ROL_Ew_Ib()     { SHIFT_L_WORD_OP(LOAD_IMM    ,1,0,0,0); }
void ROR_Ew_Ib()     { SHIFT_R_WORD_OP(LOAD_IMM    ,1,0,0,0); }
void RCL_Ew_Ib()     { SHIFT_L_WORD_OP(LOAD_IMM    ,1,1,0,0); }
void RCR_Ew_Ib()     { SHIFT_R_WORD_OP(LOAD_IMM    ,1,1,0,0); }
void SHL_Ew_Ib()     { SHIFT_L_WORD_OP(LOAD_IMM    ,0,0,0,0); }
void SHR_Ew_Ib()     { SHIFT_R_WORD_OP(LOAD_IMM    ,0,0,0,0); }
void SAR_Ew_Ib()     { SHIFT_R_WORD_OP(LOAD_IMM    ,0,0,1,0); }
void ROL_Ew_1()      { SHIFT_L_WORD_OP(1           ,1,0,0,0); }
void ROR_Ew_1()      { SHIFT_R_WORD_OP(1           ,1,0,0,0); }
void RCL_Ew_1()      { SHIFT_L_WORD_OP(1           ,1,1,0,0); }
void RCR_Ew_1()      { SHIFT_R_WORD_OP(1           ,1,1,0,0); }
void SHL_Ew_1()      { SHIFT_L_WORD_OP(1           ,0,0,0,0); }
void SHR_Ew_1()      { SHIFT_R_WORD_OP(1           ,0,0,0,0); }
void SAR_Ew_1()      { SHIFT_R_WORD_OP(1           ,0,0,1,0); }
void ROL_Ew_CL()     { SHIFT_L_WORD_OP(LOAD_REG_ECX,1,0,0,0); }
void ROR_Ew_CL()     { SHIFT_R_WORD_OP(LOAD_REG_ECX,1,0,0,0); }
void RCL_Ew_CL()     { SHIFT_L_WORD_OP(LOAD_REG_ECX,1,1,0,0); }
void RCR_Ew_CL()     { SHIFT_R_WORD_OP(LOAD_REG_ECX,1,1,0,0); }
void SHL_Ew_CL()     { SHIFT_L_WORD_OP(LOAD_REG_ECX,0,0,0,0); }
void SHR_Ew_CL()     { SHIFT_R_WORD_OP(LOAD_REG_ECX,0,0,0,0); }
void SAR_Ew_CL()     { SHIFT_R_WORD_OP(LOAD_REG_ECX,0,0,1,0); }
void ROL_Ed_Ib()     { SHIFT_L_DWORD_OP(LOAD_IMM    ,1,0,0,0); }
void ROR_Ed_Ib()     { SHIFT_R_DWORD_OP(LOAD_IMM    ,1,0,0,0); }
void RCL_Ed_Ib()     { SHIFT_L_DWORD_OP(LOAD_IMM    ,1,1,0,0); }
void RCR_Ed_Ib()     { SHIFT_R_DWORD_OP(LOAD_IMM    ,1,1,0,0); }
void SHL_Ed_Ib()     { SHIFT_L_DWORD_OP(LOAD_IMM    ,0,0,0,0); }
void SHR_Ed_Ib()     { SHIFT_R_DWORD_OP(LOAD_IMM    ,0,0,0,0); }
void SAR_Ed_Ib()     { SHIFT_R_DWORD_OP(LOAD_IMM    ,0,0,1,0); }
void ROL_Ed_1()      { SHIFT_L_DWORD_OP(1           ,1,0,0,0); }
void ROR_Ed_1()      { SHIFT_R_DWORD_OP(1           ,1,0,0,0); }
void RCL_Ed_1()      { SHIFT_L_DWORD_OP(1           ,1,1,0,0); }
void RCR_Ed_1()      { SHIFT_R_DWORD_OP(1           ,1,1,0,0); }
void SHL_Ed_1()      { SHIFT_L_DWORD_OP(1           ,0,0,0,0); }
void SHR_Ed_1()      { SHIFT_R_DWORD_OP(1           ,0,0,0,0); }
void SAR_Ed_1()      { SHIFT_R_DWORD_OP(1           ,0,0,1,0); }
void ROL_Ed_CL()     { SHIFT_L_DWORD_OP(LOAD_REG_ECX,1,0,0,0); }
void ROR_Ed_CL()     { SHIFT_R_DWORD_OP(LOAD_REG_ECX,1,0,0,0); }
void RCL_Ed_CL()     { SHIFT_L_DWORD_OP(LOAD_REG_ECX,1,1,0,0); }
void RCR_Ed_CL()     { SHIFT_R_DWORD_OP(LOAD_REG_ECX,1,1,0,0); }
void SHL_Ed_CL()     { SHIFT_L_DWORD_OP(LOAD_REG_ECX,0,0,0,0); }
void SHR_Ed_CL()     { SHIFT_R_DWORD_OP(LOAD_REG_ECX,0,0,0,0); }
void SAR_Ed_CL()     { SHIFT_R_DWORD_OP(LOAD_REG_ECX,0,0,1,0); }
void SHLD_Ew_Gw_Ib() { SHIFT_L_WORD_OP(LOAD_IMM    ,0,0,0,1);  }
void SHLD_Ew_Gw_CL() { SHIFT_L_WORD_OP(LOAD_REG_ECX,0,0,0,1);  }
void SHRD_Ew_Gw_Ib() { SHIFT_R_WORD_OP(LOAD_IMM    ,0,0,0,1);  }
void SHRD_Ew_Gw_CL() { SHIFT_R_WORD_OP(LOAD_REG_ECX,0,0,0,1);  }
void SHLD_Ed_Gd_Ib() { SHIFT_L_DWORD_OP(LOAD_IMM    ,0,0,0,1);  }
void SHLD_Ed_Gd_CL() { SHIFT_L_DWORD_OP(LOAD_REG_ECX,0,0,0,1);  }
void SHRD_Ed_Gd_Ib() { SHIFT_R_DWORD_OP(LOAD_IMM    ,0,0,0,1);  }
void SHRD_Ed_Gd_CL() { SHIFT_R_DWORD_OP(LOAD_REG_ECX,0,0,0,1);  }

void MOVSB() { STRING_OP(1,1,0,0,1,0,0,0,0); }
void MOVSW() { STRING_OP(2,1,0,0,1,0,0,0,0); }
void MOVSD() { STRING_OP(4,1,0,0,1,0,0,0,0); }
void CMPSB() { STRING_OP(1,1,1,0,0,0,1,0,0); }
void CMPSW() { STRING_OP(2,1,1,0,0,0,1,0,0); }
void CMPSD() { STRING_OP(4,1,1,0,0,0,1,0,0); }
void STOSB() { STRING_OP(1,0,0,1,1,0,0,0,0); }
void STOSW() { STRING_OP(2,0,0,1,1,0,0,0,0); }
void STOSD() { STRING_OP(4,0,0,1,1,0,0,0,0); }
void LODSB() { STRING_OP(1,1,0,0,0,1,0,0,0); }
void LODSW() { STRING_OP(2,1,0,0,0,1,0,0,0); }
void LODSD() { STRING_OP(4,1,0,0,0,1,0,0,0); }
void SCASB() { STRING_OP(1,0,1,1,0,0,1,0,0); }
void SCASW() { STRING_OP(2,0,1,1,0,0,1,0,0); }
void SCASD() { STRING_OP(4,0,1,1,0,0,1,0,0); }

void BSF_Gw_Ew() {
  IVAL res, src;
  LOAD_RM_R_WORD(src);
  res = (src & 0xFF) ? BitScanForwardTable[src & 0xFF] : 8 + BitScanForwardTable[(src >> 8) & 0xFF];
  SET_FLAGS_EAGER(((src == 0) ? EFLAGS_BIT_Z : 0));
  STORE_REG_WORD(res);
}
void BSF_Gd_Ed() {
  IVAL res, src;
  LOAD_RM_R_DWORD(src);
  res = (src & 0xFF) ? BitScanForwardTable[src & 0xFF] : ((src & 0xFF00) ? 8 + BitScanForwardTable[(src >> 8) & 0xFF] : 
        ((src & 0xFF0000) ? 16 + BitScanForwardTable[(src >> 16) & 0xFF] : 24 + BitScanForwardTable[(src >> 24) & 0xFF]));
  SET_FLAGS_EAGER(((src == 0) ? EFLAGS_BIT_Z : 0));
  STORE_REG_DWORD(res);
}
void BSR_Gw_Ew() {
  IVAL res, src;
  LOAD_RM_R_WORD(src);
  res = (src & 0xFF00) ? 8 + BitScanReverseTable[(src >> 8) & 0xFF] : BitScanReverseTable[src & 0xFF];
  SET_FLAGS_EAGER(((src == 0) ? EFLAGS_BIT_Z : 0));
  STORE_REG_WORD(res);
}
void BSR_Gd_Ed() {
  IVAL res, src;
  LOAD_RM_R_DWORD(src);
  res = (src & 0xFF000000) ? 24 + BitScanReverseTable[(src >> 24) & 0xFF] : ((src & 0xFF0000) ? 16 + BitScanReverseTable[(src >> 16) & 0xFF] : 
        ((src & 0xFF00) ? 8 + BitScanReverseTable[(src >> 8) & 0xFF] : BitScanReverseTable[src & 0xFF]));
  SET_FLAGS_EAGER(((src == 0) ? EFLAGS_BIT_Z : 0));
  STORE_REG_DWORD(res);
}

void DAA() {
  IVAL res;
  INT tmp;
  res = LOAD_REG_EAX & 0xFF;
  tmp = 0;
  if (((res & 0xF) > 0x9) || GET_AUX_FLAG) {
    res = res + 0x6;
    tmp |= EFLAGS_BIT_A;
  }
  if (((res & 0xF0) > 0x90) || (res & 0x100) || GET_CARRY_FLAG) {
    res = res + 0x60;
    tmp |= EFLAGS_BIT_C;
  }
  STORE_REG_ID_BYTE(AL, res);
  SET_FLAGS(CF_BCD, tmp, res);
}

void DAS() {
  IVAL res;
  INT tmp;
  res = LOAD_REG_EAX & 0xFF;
  tmp = 0;
  if (((res & 0xF) > 0x9) || GET_AUX_FLAG) {
    res = res - 0x6;
    tmp |= EFLAGS_BIT_A;
  }
  if (((res & 0xFF) > 0x9F) || (res & 0x100) || GET_CARRY_FLAG) {
    res = res - 0x60;
    tmp |= EFLAGS_BIT_C;
  }
  STORE_REG_ID_BYTE(AL, res);
  SET_FLAGS(CF_BCD, tmp, res);
}

void AAA() {
  IVAL res;
  INT tmp;
  res = LOAD_REG_EAX;
  if (((res & 0xF) > 0x9) || GET_AUX_FLAG) {
    res = (((res + 6) & 0x0F) | (res & 0xFF00)) + 0x100;
    tmp = EFLAGS_BIT_A | EFLAGS_BIT_C;
  } else {
    res = (res & 0xFF0F);
    tmp = 0;
  }
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS(CF_BCD, tmp, res);
}

void AAS() {
  IVAL res;
  INT tmp;
  res = LOAD_REG_EAX;
  if (((res & 0xF) > 0x9) || GET_AUX_FLAG) {
    res = (((res - 6) & 0x0F) | (res & 0xFF00)) - 0x100;
    tmp = EFLAGS_BIT_A | EFLAGS_BIT_C;
  } else {
    res = (res & 0xFF0F);
    tmp = 0;
  }
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS(CF_BCD, tmp, res);
}

// TODO: implement DIVIDE_ERROR
// NOTE: appears that flags are based on "byte" size result, even though documentation
//       considers "AX" the implied source.
void AAM_Ib() {
  IVAL src, res, base;
  src = LOAD_REG_EAX & 0xFF;
  base = LOAD_IMM_B;
  if (base) {
    res = ((src/base) << 8) | (src%base);
  } else {
    DIVIDE_ERROR;
    res = 0;
  }
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS(CF_BCD, 0, res);
}

// appears that flags are based on "byte" size result
// NOTE: P4 sets undefined flags to 0, in contrast to claims of "undocumented" flag behavior
void AAD_Ib() {
  IVAL src, res, base;
  src = LOAD_REG_EAX;
  base = LOAD_IMM_B;
  res = (src + (((src & 0x00FF00) >> 8) * base)) & 0xFF;
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS(CF_BCD, 0, res);
}

void MOV_AL_Ob() {
  IVAL src, ofs;
  ofs = LOAD_IMM;
  LOAD_MEM_BYTE(src,ofs);
  STORE_REG_ID_BYTE(AL,src);
}
void MOV_AX_Ow() {
  IVAL src, ofs;
  ofs = LOAD_IMM;
  LOAD_MEM_WORD(src,ofs);
  STORE_REG_ID_WORD(AX,src);
}
void MOV_EAX_Od() {
  IVAL src, ofs;
  ofs = LOAD_IMM;
  LOAD_MEM_DWORD(src,ofs);
  STORE_REG_ID_DWORD(EAX,src);
}
void MOV_Ob_AL() {
  IVAL src, ofs;
  ofs = LOAD_IMM;
  src = LOAD_REG_EAX;
  STORE_MEM_BYTE(ofs,src);
}
void MOV_Ow_AX() {
  IVAL src, ofs;
  ofs = LOAD_IMM;
  src = LOAD_REG_EAX;
  STORE_MEM_WORD(ofs,src);
}
void MOV_Od_EAX() {
  IVAL src, ofs;
  ofs = LOAD_IMM;
  src = LOAD_REG_EAX;
  STORE_MEM_DWORD(ofs,src);
}

void MOV_Ew_Sw() {
  IVAL val;
  val = GET_SEG_SEL; 
  STORE_RM_W_WORD(val);
}
void MOV_Sw_Ew() {
  IVAL val;
  LOAD_RM_RW_WORD(val);
  SET_SEG_SEL(val);
}

void XLAT() {
  IVAL src, ofs;
  ofs = LOAD_REG_EBX;
  if (!ADDR_SZ) {
    ofs = ofs & 0xFFFF;
  }
  ofs += (LOAD_REG_EAX & 0xFF);
  LOAD_MEM_BYTE(src,ofs);
  STORE_REG_ID_BYTE(AL,src);
}

void MUL_Eb() {
  IVAL src1, src2, res, tmp;
  src1 = LOAD_REG_EAX & 0xFF;
  LOAD_RM_R_BYTE(src2);
  res = src1*src2;
  tmp = (res & 0xFF00) ? EFLAGS_BIT_C | EFLAGS_BIT_O : 0;
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS_EAGER(tmp);
}

void MUL_Ew() {
  IVAL src1, src2, res, reshi, tmp;
  src1 = LOAD_REG_EAX & 0xFFFF;
  LOAD_RM_R_WORD(src2);
  res = src1*src2;
  reshi = (res >> 16);
  tmp = reshi ? EFLAGS_BIT_C | EFLAGS_BIT_O : 0;
  STORE_REG_ID_WORD(AX,res);
  STORE_REG_ID_WORD(DX,reshi);
  SET_FLAGS_EAGER(tmp);
}

void MUL_Ed() {
  IVAL src1, src2, reslo, reshi, tmp;
  UINT64 res;
  src1 = LOAD_REG_EAX & 0xFFFFFFFF;
  LOAD_RM_R_DWORD(src2);
  res = ((UINT64)src1)*((UINT64)src2);
  reshi = (res >> 32);
  reslo = res;
  tmp = reshi ? EFLAGS_BIT_C | EFLAGS_BIT_O : 0;
  STORE_REG_ID_DWORD(EAX,reslo);
  STORE_REG_ID_DWORD(EDX,reshi);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Eb() {
  IVAL src1, src2, res, tmp;
  src1 = LOAD_REG_EAX;
  LOAD_RM_R_BYTE(src2);
  res = ((SVAL)(INT8)src1)*((SVAL)(INT8)src2);
  tmp = res & 0xFF80;
  if ((tmp == 0xFF80) || (tmp == 0x0000)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Ew() {
  IVAL src1, src2, res, reshi, tmp;
  src1 = LOAD_REG_EAX;
  LOAD_RM_R_WORD(src2);
  res = ((SVAL)(INT16)src1)*((SVAL)(INT16)src2);
  reshi = res >> 16;
  if ((INT16)reshi == (((INT16)res) >> 15)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_ID_WORD(AX,res);
  STORE_REG_ID_WORD(DX,reshi);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Ed() {
  IVAL src1, src2, reslo, reshi, tmp;
  INT64 res;
  src1 = LOAD_REG_EAX;
  LOAD_RM_R_DWORD(src2);
  res = ((INT64)(INT32)src1)*((INT64)(INT32)src2);
  reslo = res;
  reshi = res >> 32;
  if ((INT32)reshi == (((INT32)reslo) >> 31)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_ID_DWORD(EAX,reslo);
  STORE_REG_ID_DWORD(EDX,reshi);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Gw_Ew() {
  IVAL src1, src2, res, reshi, tmp;
  src1 = LOAD_REG_WORD;
  LOAD_RM_R_WORD(src2);
  res = ((SVAL)(INT16)src1)*((SVAL)(INT16)src2);
  reshi = res >> 16;
  if ((INT16)reshi == (((INT16)res) >> 15)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_WORD(res);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Gd_Ed() {
  IVAL src1, src2, reslo, reshi, tmp;
  INT64 res;
  src1 = LOAD_REG_DWORD;
  LOAD_RM_R_DWORD(src2);
  res = ((INT64)(INT32)src1)*((INT64)(INT32)src2);
  reslo = res;
  reshi = res >> 32;
  if ((INT32)reshi == (((INT32)reslo) >> 31)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_DWORD(reslo);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Gw_Ew_I() {
  IVAL src1, src2, res, reshi, tmp;
  src1 = LOAD_IMM_W;
  LOAD_RM_R_WORD(src2);
  res = ((SVAL)(INT16)src1)*((SVAL)(INT16)src2);
  reshi = res >> 16;
  if ((INT16)reshi == (((INT16)res) >> 15)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_WORD(res);
  SET_FLAGS_EAGER(tmp);
}

void IMUL_Gd_Ed_I() {
  IVAL src1, src2, reslo, reshi, tmp;
  INT64 res;
  src1 = LOAD_IMM_D;
  LOAD_RM_R_DWORD(src2);
  res = ((INT64)(INT32)src1)*((INT64)(INT32)src2);
  reslo = res;
  reshi = res >> 32;
  if ((INT32)reshi == (((INT32)reslo) >> 31)) {
    tmp = 0;
  } else {
    tmp = EFLAGS_BIT_C | EFLAGS_BIT_O;
  }
  STORE_REG_DWORD(reslo);
  SET_FLAGS_EAGER(tmp);
}

void DIV_Eb() {
  IVAL res,rem, src1, src2;
  src1 = LOAD_REG_EAX & 0xFFFF;
  LOAD_RM_R_BYTE(src2);
  if (src2 == 0) {
    DIVIDE_ERROR;
  }
  res = src1 / src2;
  rem = src1 % src2;
  if (res & ~0xFF) {
    DIVIDE_ERROR;
  }
  res |= (rem << 8);
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS_EAGER(0);
}

void DIV_Ew() {
  IVAL res,rem, src1, src2;
  src1 = ((LOAD_REG_EDX & 0xFFFF) << 16) | (LOAD_REG_EAX & 0xFFFF);
  LOAD_RM_R_WORD(src2);
  if (src2 == 0) {
    DIVIDE_ERROR;
  }
  res = src1 / src2;
  rem = src1 % src2;
  if (res & ~0xFFFF) {
    DIVIDE_ERROR;
  }
  STORE_REG_ID_WORD(AX,res);
  STORE_REG_ID_WORD(DX,rem);
  SET_FLAGS_EAGER(0);
}

void DIV_Ed() {
  UINT64 res,rem,src1,src2;
  src1 = (((UINT64)LOAD_REG_EDX) << 32) | LOAD_REG_EAX;
  LOAD_RM_R_DWORD(src2);
  if (src2 == 0) {
    DIVIDE_ERROR;
  }
  res = src1 / src2;
  rem = src1 % src2;
  if ((res >> 32) != 0) {
    DIVIDE_ERROR;
  }
  STORE_REG_ID_DWORD(EAX,res);
  STORE_REG_ID_DWORD(EDX,rem);
  SET_FLAGS_EAGER(0);
}

void IDIV_Eb() {
  IVAL tmp;
  INT32 src1,src2,res,rem;
  src1 = (INT32)(LOAD_REG_EAX & 0xFFFF);
  LOAD_RM_R_BYTE(tmp);
  src2 = (INT32)(INT8)tmp;
  if (src2 == 0) {
    DIVIDE_ERROR;
  }
  res = src1 / src2;
#if CFG_NATIVE_MODS_NEGATIVE_DIFFERENTLY
#error TODO: modulus on negative numbers convention issues
#else
  rem = src1 % src2;
#endif
  if (res != (INT32)((INT8)res) ) {
    DIVIDE_ERROR;
  }
  res |= (rem << 8);
  STORE_REG_ID_WORD(AX,res);
  SET_FLAGS_EAGER(0);
}

void IDIV_Ew() {
  IVAL tmp;
  INT32 src2,res,rem;
  INT32 src1 = (INT32)(((LOAD_REG_EDX & 0xFFFF) << 16) | (LOAD_REG_EAX & 0xFFFF));
  LOAD_RM_R_WORD(tmp);
  src2 = (INT32)(INT16)tmp;
  if (src2 == 0) {
    DIVIDE_ERROR;
  } else if ((src2 == -1)  && (src1 == (INT32)0x80000000)) {
    // we can't safely perform 32-bit division in this corner case
    DIVIDE_ERROR;
  }
  res = ((INT32)src1) / src2;
#if CFG_NATIVE_MODS_NEGATIVE_DIFFERENTLY
#error TODO: modulus on negative numbers convention issues
#else
  rem = src1 % src2;
#endif
  if (res != ((INT16)res) ) {
    DIVIDE_ERROR;
  }
  STORE_REG_ID_WORD(AX,res);
  STORE_REG_ID_WORD(DX,rem);
  SET_FLAGS_EAGER(0);
}

void IDIV_Ed() {
  INT64 src2,res,rem;
  INT64 src1 = (((INT64)LOAD_REG_EDX) << 32) | LOAD_REG_EAX;
  LOAD_RM_R_DWORD(src2);
  src2 = (INT64)(INT32)src2;
  if (src2 == 0) {
    DIVIDE_ERROR;
  } else if ((src2 == (INT64)-1) && (src1 == (INT64)0x8000000000000000LL)) {
    // we can't safely perform division in this corner case
    DIVIDE_ERROR;
  }
  res = ((INT64)src1) / src2;
#if CFG_NATIVE_MODS_NEGATIVE_DIFFERENTLY
#error TODO: modulus on negative numbers convention issues
#else
  rem = src1 % src2;
#endif
  if (res != ((INT32)res) ) {
    DIVIDE_ERROR;
  }
  STORE_REG_ID_DWORD(EAX,res);
  STORE_REG_ID_DWORD(EDX,rem);
  SET_FLAGS_EAGER(0);
}

void PUSH_CSw() { PUSH_OP(val=GET_SEG_SEL_ID(CS),0); }
void PUSH_CSd() { PUSH_OP(val=GET_SEG_SEL_ID(CS),1); }
void PUSH_DSw() { PUSH_OP(val=GET_SEG_SEL_ID(DS),0); }
void PUSH_DSd() { PUSH_OP(val=GET_SEG_SEL_ID(DS),1); }
void PUSH_ESw() { PUSH_OP(val=GET_SEG_SEL_ID(ES),0); }
void PUSH_ESd() { PUSH_OP(val=GET_SEG_SEL_ID(ES),1); }
void PUSH_FSw() { PUSH_OP(val=GET_SEG_SEL_ID(FS),0); }
void PUSH_FSd() { PUSH_OP(val=GET_SEG_SEL_ID(FS),1); }
void PUSH_GSw() { PUSH_OP(val=GET_SEG_SEL_ID(GS),0); }
void PUSH_GSd() { PUSH_OP(val=GET_SEG_SEL_ID(GS),1); }
void PUSH_SSw() { PUSH_OP(val=GET_SEG_SEL_ID(SS),0); }
void PUSH_SSd() { PUSH_OP(val=GET_SEG_SEL_ID(SS),1); }

void POP_DSw()  { POP_OP(SET_SEG_SEL_ID(DS,val),0,0); }
void POP_DSd()  { POP_OP(SET_SEG_SEL_ID(DS,val),1,0); }
void POP_ESw()  { POP_OP(SET_SEG_SEL_ID(ES,val),0,0); }
void POP_ESd()  { POP_OP(SET_SEG_SEL_ID(ES,val),1,0); }
void POP_FSw()  { POP_OP(SET_SEG_SEL_ID(FS,val),0,0); }
void POP_FSd()  { POP_OP(SET_SEG_SEL_ID(FS,val),1,0); }
void POP_GSw()  { POP_OP(SET_SEG_SEL_ID(GS,val),0,0); }
void POP_GSd()  { POP_OP(SET_SEG_SEL_ID(GS,val),1,0); }
void POP_SSw()  { POP_OP(SET_SEG_SEL_ID(SS,val),0,0); }
void POP_SSd()  { POP_OP(SET_SEG_SEL_ID(SS,val),1,0); }

void PUSH_AX()  { PUSH_OP(val=LOAD_REG_EAX,0); }
void PUSH_EAX() { PUSH_OP(val=LOAD_REG_EAX,1); }
void PUSH_CX()  { PUSH_OP(val=LOAD_REG_ECX,0); }
void PUSH_ECX() { PUSH_OP(val=LOAD_REG_ECX,1); }
void PUSH_DX()  { PUSH_OP(val=LOAD_REG_EDX,0); }
void PUSH_EDX() { PUSH_OP(val=LOAD_REG_EDX,1); }
void PUSH_BX()  { PUSH_OP(val=LOAD_REG_EBX,0); }
void PUSH_EBX() { PUSH_OP(val=LOAD_REG_EBX,1); }
void PUSH_SP()  { PUSH_OP(val=LOAD_REG_ESP,0); }
void PUSH_ESP() { PUSH_OP(val=LOAD_REG_ESP,1); }
void PUSH_BP()  { PUSH_OP(val=LOAD_REG_EBP,0); }
void PUSH_EBP() { PUSH_OP(val=LOAD_REG_EBP,1); }
void PUSH_SI()  { PUSH_OP(val=LOAD_REG_ESI,0); }
void PUSH_ESI() { PUSH_OP(val=LOAD_REG_ESI,1); }
void PUSH_DI()  { PUSH_OP(val=LOAD_REG_EDI,0); }
void PUSH_EDI() { PUSH_OP(val=LOAD_REG_EDI,1); }

void POP_AX()   { POP_OP(STORE_REG_ID_WORD(AX,val),0,0); }
void POP_EAX()  { POP_OP(STORE_REG_ID_DWORD(EAX,val),1,0); }
void POP_CX()   { POP_OP(STORE_REG_ID_WORD(CX,val),0,0); }
void POP_ECX()  { POP_OP(STORE_REG_ID_DWORD(ECX,val),1,0); }
void POP_DX()   { POP_OP(STORE_REG_ID_WORD(DX,val),0,0); }
void POP_EDX()  { POP_OP(STORE_REG_ID_DWORD(EDX,val),1,0); }
void POP_BX()   { POP_OP(STORE_REG_ID_WORD(BX,val),0,0); }
void POP_EBX()  { POP_OP(STORE_REG_ID_DWORD(EBX,val),1,0); }
void POP_SP()   { POP_OP(STORE_REG_ID_WORD(SP,val),0,0); }
void POP_ESP()  { POP_OP(STORE_REG_ID_DWORD(ESP,val),1,1); }
void POP_BP()   { POP_OP(STORE_REG_ID_WORD(BP,val),0,0); }
void POP_EBP()  { POP_OP(STORE_REG_ID_DWORD(EBP,val),1,0); }
void POP_SI()   { POP_OP(STORE_REG_ID_WORD(SI,val),0,0); }
void POP_ESI()  { POP_OP(STORE_REG_ID_DWORD(ESI,val),1,0); }
void POP_DI()   { POP_OP(STORE_REG_ID_WORD(DI,val),0,0); }
void POP_EDI()  { POP_OP(STORE_REG_ID_DWORD(EDI,val),1,0); }

void PUSH_Iw()  { PUSH_OP(val=LOAD_IMM_W,0); }
void PUSH_Id()  { PUSH_OP(val=LOAD_IMM_D,1); }
void PUSH_Ew()  { PUSH_OP(LOAD_RM_R_WORD(val),0); }
void PUSH_Ed()  { PUSH_OP(LOAD_RM_R_DWORD(val),1); }

void POP_Ew() {
  UINT is_esp_dst = ((X86_TMP(mod) == 0xC0) && (X86_TMP(rm) == ESP));
  UINT esp_in_ea_factor = ((X86_TMP(mod) != 0xC0) && ADDR_SZ && (X86_TMP(rm) == 0x4) && ((X86_TMP(sib) & 0x7) == 4));
  UINT fStackSize = GetSegAttr(SS,SEG_ATTR_B);

  esp_in_ea_factor *= (fStackSize ? 4 : 2);

  X86_TMP(va) += esp_in_ea_factor;

  POP_OP(STORE_RM_WORD(val),0,is_esp_dst);
}

void POP_Ed() {
  UINT is_esp_dst = ((X86_TMP(mod) == 0xC0) && (X86_TMP(rm) == ESP));
  UINT esp_in_ea_factor = ((X86_TMP(mod) != 0xC0) && ADDR_SZ && (X86_TMP(rm) == 0x4) && ((X86_TMP(sib) & 0x7) == 4));
  UINT fStackSize = GetSegAttr(SS,SEG_ATTR_B);

  esp_in_ea_factor *= (fStackSize ? 4 : 2);

  X86_TMP(va) += esp_in_ea_factor;

  POP_OP(STORE_RM_DWORD(val),1,is_esp_dst);
}

void PUSHF() { PUSH_OP(val=(getFlags() & 0xFCFFFF),0); }
void PUSHFD() { PUSH_OP(val=(getFlags() & 0xFCFFFF),1); }
void POPF() { POP_OP(setFlags((getFlags() & (0xFFFF0000 | ~EFLAGS_BITS_USER)) | (val & EFLAGS_BITS_USER)), 0, 0); }
void POPFD() { POP_OP(setFlags((getFlags() & ~EFLAGS_BITS_USER) | (val & EFLAGS_BITS_USER)), 1, 0); }


void LDS_Gw_Mp() { LOAD_SEG_OFS_WORD_OP(DS); }
void LES_Gw_Mp() { LOAD_SEG_OFS_WORD_OP(ES); }
void LFS_Gw_Mp() { LOAD_SEG_OFS_WORD_OP(FS); }
void LGS_Gw_Mp() { LOAD_SEG_OFS_WORD_OP(GS); }
void LSS_Gw_Mp() { LOAD_SEG_OFS_WORD_OP(SS); }

void LDS_Gd_Mp() { LOAD_SEG_OFS_DWORD_OP(DS); }
void LES_Gd_Mp() { LOAD_SEG_OFS_DWORD_OP(ES); }
void LFS_Gd_Mp() { LOAD_SEG_OFS_DWORD_OP(FS); }
void LGS_Gd_Mp() { LOAD_SEG_OFS_DWORD_OP(GS); }
void LSS_Gd_Mp() { LOAD_SEG_OFS_DWORD_OP(SS); }

void BOUND_Gw_Ma() {
  IVAL src, lo, hi;
  if (X86_TMP(mod) == 0xC0) {
    INVALIDOP();
  }
  src = LOAD_REG_WORD;
  lo = LOAD_VIRTUAL_UNALIGNED_WORD(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ);
  hi = LOAD_VIRTUAL_UNALIGNED_WORD(X86_TMP(va)+2,X86_TMP(segOverride),ACCESS_READ);
  if ((((INT16)src) < ((INT16)lo)) || (((INT16)src) > ((INT16)hi))) {
    BOUNDS_ERROR;
  }
}
void BOUND_Gd_Ma() {
  IVAL src, lo, hi;
  if (X86_TMP(mod) == 0xC0) {
    INVALIDOP();
  }
  src = LOAD_REG_DWORD;
  lo = LOAD_VIRTUAL_UNALIGNED_DWORD(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ);
  hi = LOAD_VIRTUAL_UNALIGNED_DWORD(X86_TMP(va)+4,X86_TMP(segOverride),ACCESS_READ);
  if ((((INT32)src) < ((INT32)lo)) || (((INT32)src) > ((INT32)hi))) {
    BOUNDS_ERROR;
  }
}

// TODO: understand differences for effective addresses wrt operand and address sizes
void LEA_Gw_M() {
  STORE_REG_WORD(X86_TMP(va));
}

void LEA_Gd_M() {
  STORE_REG_DWORD(X86_TMP(va));
}

void LEAVE() {
  IVAL val, ptr;
  INT fStackSize;
  fStackSize = GetSegAttr(SS,SEG_ATTR_B);
  ptr = LOAD_REG_EBP;
  if (!fStackSize) {
    ptr = ptr & 0xFFFF;
  }
  SEG_OVERRIDE(SS);
  LOAD_MEM_WORD(val,ptr);
  ptr += 2;
  if (fStackSize) {
    STORE_REG_ID_DWORD(ESP,ptr);
  } else {
    STORE_REG_ID_WORD(SP, ptr);
  }
  STORE_REG_ID_WORD(BP, val);
}

void LEAVED() {
  IVAL val, ptr;
  INT fStackSize;
  fStackSize = GetSegAttr(SS,SEG_ATTR_B);
  ptr = LOAD_REG_EBP;
  if (!fStackSize) {
    ptr = ptr & 0xFFFF;
  }
  SEG_OVERRIDE(SS);
  LOAD_MEM_DWORD(val, ptr);
  ptr += 4;
  if (fStackSize) {
    STORE_REG_ID_DWORD(ESP,ptr);
  } else {
    STORE_REG_ID_WORD(SP, ptr);
  }
  STORE_REG_ID_DWORD(EBP, val);
}

void BT_Ew_Gw()  { BIT_TEST_WORD_OP(0,0,LockInvalid); }
void BT_Ed_Gd()  { BIT_TEST_DWORD_OP(0,0,LockInvalid); }
void BTS_Ew_Gw() { BIT_TEST_WORD_OP(0,1,LockBtsH); }
void BTS_Ed_Gd() { BIT_TEST_DWORD_OP(0,1,LockBtsW); }
void BTR_Ew_Gw() { BIT_TEST_WORD_OP(0,2,LockBtrH); }
void BTR_Ed_Gd() { BIT_TEST_DWORD_OP(0,2,LockBtrW); }
void BTC_Ew_Gw() { BIT_TEST_WORD_OP(0,3,LockBtcH); }
void BTC_Ed_Gd() { BIT_TEST_DWORD_OP(0,3,LockBtcW); }
void BT_Ew_Ib()  { BIT_TEST_WORD_OP(1,0,LockInvalid); }
void BT_Ed_Ib()  { BIT_TEST_DWORD_OP(1,0,LockInvalid); }
void BTS_Ew_Ib() { BIT_TEST_WORD_OP(1,1,LockBtsH); }
void BTS_Ed_Ib() { BIT_TEST_DWORD_OP(1,1,LockBtsW); }
void BTR_Ew_Ib() { BIT_TEST_WORD_OP(1,2,LockBtrH); }
void BTR_Ed_Ib() { BIT_TEST_DWORD_OP(1,2,LockBtrW); }
void BTC_Ew_Ib() { BIT_TEST_WORD_OP(1,3,LockBtcH); }
void BTC_Ed_Ib() { BIT_TEST_DWORD_OP(1,3,LockBtcW); }


// TODO: these ops
void PUSHA() { ASSERT(UNREACHED); }
void PUSHAD() {
    IVAL temp = X86_CTXT(GPR[ESP]);
    PUSH_EAX();
    PUSH_ECX();    
    PUSH_EDX();
    PUSH_EBX();
    PUSH_OP(val=temp,1);
    PUSH_EBP();    
    PUSH_ESI();
    PUSH_EDI();
}
void POPA() { ASSERT(UNREACHED); }
void POPAD() {
    POP_EDI();
    POP_ESI();
    POP_EBP();
    X86_CTXT(GPR[ESP]) += 4;
    POP_EBX();
    POP_EDX();
    POP_ECX();
    POP_EAX();
}
void ENTER_Iw_Ib() { ASSERT(UNREACHED); }
void ARPL_Ew_Gw() { ASSERT(UNREACHED); }

#ifdef WITH_PIN_SUPPORT
void MOVQ_XMM_ECX() {
    STORE_VIRTUAL_UNALIGNED_QWORD(X86_CTXT(xmm0), X86_CTXT(GPR[ECX]),X86_TMP(segOverride),0);       
}

void MOVQ_ECX_XMM() {
    X86_CTXT(xmm0) = LOAD_VIRTUAL_UNALIGNED_QWORD(X86_CTXT(GPR[ECX]),X86_TMP(segOverride),0);        
}

void MOVQ_XMM_IMM() {
// this instruction is used by pin but in no meaningful way.
/*
    IVAL src;
    src = LOAD_IMM_D;
    STORE_VIRTUAL_UNALIGNED_QWORD(X86_CTXT(xmm0), src ,X86_TMP(segOverride),0);       
*/
}
#endif //WITH_PIN_SUPPORT


// TODO: PUSH/POP SS, ESP have special ordering concerns that are broken above (particularly POP <ModRM> and POP esp)
// TODO: OF for SHLD/SHRD
